THIS WORK IS YET TO BE REVIEWED

1 Introduction

This paper contains estimates for the reproductive number \(R_{t,m}\) over time \(t\) in various countries \(m\). It also shows the same for provinces within South Africa.. This is done using the methodology as described in [1]. These have been implemented in R using EpiEstim package [2] which is what is used here.

This paper and it’s results should be updated roughly daily and is available online.

2 Updates

As this paper is updated over time this section will summarise significant changes. The code producing this paper is tracked using Git. The Git commit hash for this project at the time of generating this paper was 6cbf8f93c9af4946f466d49f7450d7c3349934a9.

2020-06-12

  • Initial version.

2020-06-13

  • Combine the version for South Africa and other countries.

3 Libraries

The project uses the following libraries.

require(EpiEstim)
require(EnvStats)
require(ggplot2)
require(ggpubr)
require(lubridate)
require(utils)
require(httr)
require(dplyr)
require(tidyr)
require(scales)

4 Countries

All countries available in the data will be analysed but also the provinces of South Africa. This paper produces charts for the following countries only:

country_codes = c("BR", # Brazil
              "CA", # Canada
              "CL", # Chile
              "IN", # India
              "IE", # Ireland
              "IT", # Italy
              "PE", # Peru
              "ZA", # South Africa
              "ES", # Spain
              "UK" # United Kingdom
              )

5 Data

5.1 South Africa

5.1.1 Download

Data is downloaded from the Git repository associated with [3].

# Provincial Deaths
GET(url = "https://raw.githubusercontent.com/dsfsi/covid19za/master/data/covid19za_provincial_cumulative_timeline_deaths.csv", write_disk("covid19za_provincial_cumulative_timeline_deaths.csv", 
    overwrite = TRUE))
Response [https://raw.githubusercontent.com/dsfsi/covid19za/master/data/covid19za_provincial_cumulative_timeline_deaths.csv]
  Date: 2020-06-14 13:59
  Status: 200
  Content-Type: text/plain; charset=utf-8
  Size: 7.41 kB
<ON DISK>  C:\Users\lrossou\Desktop\COVID-19\rt_estimates\covid19za_provincial_cumulative_timeline_deaths.csv
# Provincial Cases
GET(url = "https://raw.githubusercontent.com/dsfsi/covid19za/master/data/covid19za_provincial_cumulative_timeline_confirmed.csv", write_disk("covid19za_provincial_cumulative_timeline_confirmed.csv", 
    overwrite = TRUE))
Response [https://raw.githubusercontent.com/dsfsi/covid19za/master/data/covid19za_provincial_cumulative_timeline_confirmed.csv]
  Date: 2020-06-14 13:59
  Status: 200
  Content-Type: text/plain; charset=utf-8
  Size: 9.33 kB
<ON DISK>  C:\Users\lrossou\Desktop\COVID-19\rt_estimates\covid19za_provincial_cumulative_timeline_confirmed.csv

5.1.2 Data preperation

First read in the data from the downloaded comma-separated values text file.

# Read from CSVs above
data_za_cases <- read.csv("covid19za_provincial_cumulative_timeline_confirmed.csv", stringsAsFactors = FALSE)
data_za_deaths <- read.csv("covid19za_provincial_cumulative_timeline_deaths.csv", stringsAsFactors = FALSE)

In the case data file row 21 and 32 contain no provincial details. We estimated it by spreading the national total to the provinces in proportion to a mixture of the prior day and the next day.

data_za_cases[21, c("EC", "FS", "GP", "KZN", "LP", "MP", "NC", "NW", "WC", "UNKNOWN")] <- colSums(data_za_cases[c(20, 22), c("EC", "FS", "GP", "KZN", 
    "LP", "MP", "NC", "NW", "WC", "UNKNOWN")])/sum(data_za_cases[c(20, 22), ]$total) * data_za_cases[21, ]$total
data_za_cases[32, c("EC", "FS", "GP", "KZN", "LP", "MP", "NC", "NW", "WC", "UNKNOWN")] <- colSums(data_za_cases[c(31, 33), c("EC", "FS", "GP", "KZN", 
    "LP", "MP", "NC", "NW", "WC", "UNKNOWN")])/sum(data_za_cases[c(31, 33), ]$total) * data_za_cases[32, ]$total

The following function will be applied to case and death data. In this function the following occurs:

  1. Scale up the per province data for unknown values.
  2. This results in provincial data which are not whole numbers. These are rounded to the nearest whole number.
  3. A SA column is added as the sum of the new per province data.
  4. Data is formatted and disaggregated such that item represents the incremental cases or deaths rather than cumulative figures.
  5. Data is filled with data (albeit with 0 cases or deaths) for all dates in the range.
  6. Any incremental case or death counts that are negative are zeroeised.
  7. New cumulative figures are calculated.
fix_data_za <- function(data_za, start_date = as.Date("2020-03-01"), end_date = as.Date("2020-03-31")) {
    # Scale provinces by scale factor (assume unknown are in proportion)
    data_za[, c("EC", "FS", "GP", "KZN", "LP", "MP", "NC", "NW", "WC")] <- data_za[, c("EC", "FS", "GP", "KZN", "LP", "MP", "NC", "NW", "WC")] * (1 + 
        data_za$UNKNOWN/rowSums(data_za[, c("EC", "FS", "GP", "KZN", "LP", "MP", "NC", "NW", "WC")]))
    
    # Only select columns we need
    data_za <- data_za %>% select("date", "EC", "FS", "GP", "KZN", "LP", "MP", "NC", "NW", "WC")
    data_za$date <- as.Date(data_za$date, "%d-%m-%Y")
    
    # Round data so we have integer cases
    data_za[, c("EC", "FS", "GP", "KZN", "LP", "MP", "NC", "NW", "WC")] <- round(data_za[, c("EC", "FS", "GP", "KZN", "LP", "MP", "NC", "NW", "WC")], 
        0)
    
    # Calculate a new SA column
    data_za$SA <- rowSums(data_za[, c("EC", "FS", "GP", "KZN", "LP", "MP", "NC", "NW", "WC")])
    
    # 'Melt' the data
    data_za <- pivot_longer(data_za, cols = c("EC", "FS", "GP", "KZN", "LP", "MP", "NC", "NW", "WC", "SA"), names_to = "province", values_to = "count")
    
    # Getting daily data from the cumulative data set
    data_za = data_za %>% group_by(province) %>% arrange(date) %>% mutate(count = count - lag(count, default = 0)) %>% ungroup()
    
    # add missing dates
    all_dates <- expand_grid(date = seq(start_date, end_date, 1), province = levels(as.factor(data_za$province)))
    
    # join
    data_za <- left_join(all_dates, data_za, by = c("date", "province"))
    
    # province factor
    data_za$province <- as.factor(data_za$province)
    
    # 0 for NAs
    data_za$count <- ifelse(is.na(data_za$count), 0, data_za$count)
    
    # remove negatives
    data_za$count <- ifelse(data_za$count < 0, 0, data_za$count)
    
    # get cumulative counts
    data_za <- data_za %>% group_by(province) %>% mutate(cumulative_count = cumsum(count)) %>% ungroup()
    
    return(data_za)
}

Below we use the function above to process deaths and cases and then combine them into a single dataset.

start_date <- min(as.Date(data_za_cases$date, "%d-%m-%Y"))
end_date <- max(as.Date(data_za_cases$date, "%d-%m-%Y"))
data_za_cases <- fix_data_za(data_za_cases, start_date = start_date, end_date = end_date)
data_za_deaths <- fix_data_za(data_za_deaths, start_date = start_date, end_date = end_date)

data_za_cases <- cbind("cases", data_za_cases)
data_za_deaths <- cbind("deaths", data_za_deaths)
colnames(data_za_cases)[1] <- "type"
colnames(data_za_deaths)[1] <- "type"

# combined
data_za <- rbind(data_za_cases, data_za_deaths)

# remove data sets no longer needed
rm("data_za_cases", "data_za_deaths", "start_date", "end_date", "fix_data_za")

5.2 Other countries

5.2.1 Download

Data for other countries are downloaded from [4].

GET(url = "https://opendata.ecdc.europa.eu/covid19/casedistribution/csv", write_disk("ecdc_casedistribution.csv", overwrite = TRUE))
Response [https://opendata.ecdc.europa.eu/covid19/casedistribution/csv/]
  Date: 2020-06-14 13:59
  Status: 200
  Content-Type: application/octet-stream
  Size: 1.37 MB
<ON DISK>  C:\Users\lrossou\Desktop\COVID-19\rt_estimates\ecdc_casedistribution.csv

5.2.2 Data preperation

First read in the data from the downloaded comma-separated values text file.

# Read from CSVs above
data <- read.csv("ecdc_casedistribution.csv", stringsAsFactors = FALSE)

Update data by doing the following:

  1. Format dates.
  2. Set column names.
  3. Add data where dates were skipped (with 0 cases or deaths)
  4. “Melt” the data down.
  5. Set fields as factors as needed.
  6. Get cumulative numbers.
# get date
data$date <- as.Date(data$dateRep, format = "%d/%m/%Y")

# get cases and deaths
data <- data %>% select(continentExp, date, geoId, countriesAndTerritories, cases, deaths)

# column names
colnames(data) <- c("continent", "date", "country_code", "country", "cases", "deaths")

# add 0 data for dates that were skipped
for (c in levels(as.factor(data$country))) {
    c_dates <- (data %>% filter(country == c))$date
    check_dates <- seq(min(c_dates), max(c_dates), by = 1)
    missing_dates <- check_dates[!check_dates %in% c_dates]
    continent <- (data %>% filter(country == c))$continent[1]
    country_code <- (data %>% filter(country == c))$country_code[1]
    if (length(missing_dates) > 0) {
        missing_data <- data.frame(continent = continent, date = missing_dates, country_code = country_code, country = rep(c, length(missing_dates)), 
            cases = rep(0, length(missing_dates)), deaths = rep(0, length(missing_dates)))
        data <- rbind(data, missing_data)
    }
}

# 'Melt' the data
data <- pivot_longer(data, cols = c("cases", "deaths"), names_to = "type", values_to = "count")

# fields as factors
data$continent <- as.factor(data$continent)
data$country_code <- as.factor(data$country_code)
data$country <- as.factor(data$country)
data$type <- as.factor(data$type)

# make sure it is a data frame
data <- as.data.frame(data)

# zeroise counts below 0
data$count <- ifelse(data$count < 0, 0, data$count)

# order and get group by
data <- data %>% group_by(country_code, country, type) %>% arrange(country_code, country, type, date) %>% mutate(cumulative_count = cumsum(count)) %>% 
    ungroup()

5.3 Google Mobility Data

Google released mobility data indexes that deviations from a baseline of movement during 3 January to 6 February 2020 [5]. This data is downloaded and prepared for linking to our estimates of \(R_{t,m}\).

5.3.1 Download

The data is made available as a comma-separated file which is updated regularly but not daily. Data is sometimes up to a week behind.

GET(url = "https://www.gstatic.com/covid19/mobility/Global_Mobility_Report.csv", write_disk("google_mobility_data.csv", overwrite = TRUE))
Response [https://www.gstatic.com/covid19/mobility/Global_Mobility_Report.csv]
  Date: 2020-06-14 13:59
  Status: 200
  Content-Type: text/csv
  Size: 35.6 MB
<ON DISK>  C:\Users\lrossou\Desktop\COVID-19\rt_estimates\google_mobility_data.csv

5.3.2 Preparing the mobility data

First read in the file:

# Read from CSVs above
google_mobility_data <- read.csv("google_mobility_data.csv", stringsAsFactors = FALSE, na.strings = "")

Below the data is prepared by doing the following:

  1. Fixing dates
  2. Retaining only country level mobility data, except for South Africa where country and provincial data is retained.
  3. Renaming the fields consistently.
  4. Various transformations to make joining to other data easier later.
# fix dates
google_mobility_data$date <- as.Date(google_mobility_data$date, format = "%Y-%m-%d")

# keep only country level data but keep all data for ZA
google_mobility_data <- google_mobility_data %>% filter(country_region_code == "ZA" | is.na(sub_region_1))

# pick only the fields required
google_mobility_data <- google_mobility_data %>% select(country_region_code, country_region, sub_region_1, iso_3166_2_code, date, retail_and_recreation_percent_change_from_baseline, 
    grocery_and_pharmacy_percent_change_from_baseline, parks_percent_change_from_baseline, transit_stations_percent_change_from_baseline, workplaces_percent_change_from_baseline, 
    residential_percent_change_from_baseline)

# rename the fields
colnames(google_mobility_data) <- c("country_code", "country", "province", "province_code", "date", "retail_and_recreation", "grocery_and_pharmacy", 
    "parks", "transit_stations", "workplaces", "residential")

# move province data to country for ease later
google_mobility_data$country_code[!is.na(google_mobility_data$province_code)] <- google_mobility_data$province_code[!is.na(google_mobility_data$province_code)]
google_mobility_data$country[!is.na(google_mobility_data$province_code)] <- google_mobility_data$province[!is.na(google_mobility_data$province_code)]

# rename KZN consistently
google_mobility_data$country_code[google_mobility_data$country_code == "ZA-NL"] <- "ZA-KZN"

# drop province columns
google_mobility_data <- google_mobility_data[, c(-3, -4)]

# divide indexes by 100 to have numbers between 0 and 1
google_mobility_data[, 4:9] <- google_mobility_data[, 4:9]/100

# fix GB code to UK
google_mobility_data$country_code[google_mobility_data$country_code == "GB"] <- "UK"

# make factors
google_mobility_data$country_code <- as.factor(google_mobility_data$country_code)
google_mobility_data$country <- as.factor(google_mobility_data$country)

Further to the above we also calculate an average mobility index per [6]. This index averages the mobility indexes but exclude the following indexes:

  • Residential index is excluded as it’s less likely to drive the epidemic (and is inversely correlated),
  • Mobility in parks is also unlikely to be a driving factor.
google_mobility_data$average_mobility <- (google_mobility_data$retail_and_recreation + google_mobility_data$grocery_and_pharmacy + google_mobility_data$transit_stations + 
    google_mobility_data$workplaces)/4

5.4 Combine South African data with other data

To simplify further analysis it’s easier to combine all data in a single dataset:

# Add a 'continent' field to data_za and combine
data_za <- cbind("SA", data_za)
colnames(data_za)[1] <- "continent"
colnames(data_za)[4] <- "country"
data_za$country_code <- paste0("ZA-", data_za$country)
data <- rbind(data, data_za)

# remove all objects except what we need
rm(list = ls()[!ls() %in% c("data", "country_codes", "google_mobility_data")])

6 Basic Exploration

6.1 South Africa

Below we plot cumulative case count on a log scale by province:

ggplot(data %>% filter(continent == "SA" & type == "cases" & cumulative_count > 0), aes(x = date, y = cumulative_count)) + geom_line(aes(color = country), 
    size = 1) + scale_y_log10(labels = comma) + ggtitle("Cumulative Cases by Province") + xlab("Date") + ylab("Cumulative Cases") + theme_bw() + theme(axis.text.x = element_text(angle = 45, 
    hjust = 1), legend.position = "right") + guides(fill = guide_legend(ncol = 1)) + scale_color_hue(l = 50)

Below we plot the cumulative deaths by province on a log scale:

ggplot(data %>% filter(continent == "SA" & type == "deaths" & cumulative_count > 0), aes(x = date, y = cumulative_count)) + geom_line(aes(color = country), 
    size = 1) + scale_y_log10(labels = comma) + ggtitle("Cumulative Deaths by Province") + xlab("Date") + ylab("Cumulative Deaths") + theme_bw() + theme(axis.text.x = element_text(angle = 45, 
    hjust = 1), legend.position = "right") + guides(fill = guide_legend(ncol = 1)) + scale_color_hue(l = 50)

Below we plot average mobility indexes by province:

ggplot(data = google_mobility_data[substr(as.character(google_mobility_data$country_code), 1, 2) == "ZA", ], aes(x = date, y = average_mobility, colour = country)) + 
    geom_line() + scale_color_hue(l = 50) + theme_bw()

6.2 Other Countries

Below we plot cumulative case count on a log scale by country:

ggplot(data %>% filter(continent != "SA" & type == "cases" & country_code %in% country_codes & cumulative_count > 0), aes(x = date, y = cumulative_count)) + 
    geom_line(aes(color = country), size = 1) + scale_y_log10(labels = comma) + ggtitle("Cumulative Cases by Country") + xlab("Date") + ylab("Cumulative Cases") + 
    theme_bw() + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position = "right") + guides(fill = guide_legend(ncol = 1)) + scale_color_hue(l = 50)

Below we plot the cumulative deaths by country on a log scale:

ggplot(data %>% filter(continent != "SA" & type == "deaths" & country_code %in% country_codes & cumulative_count > 0), aes(x = date, y = cumulative_count)) + 
    geom_line(aes(color = country), size = 1) + scale_y_log10(labels = comma) + ggtitle("Cumulative Deaths by Country") + xlab("Date") + ylab("Cumulative Deaths") + 
    theme_bw() + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position = "right") + guides(fill = guide_legend(ncol = 1)) + scale_color_hue(l = 50)

Below we plot average mobility indexes by country:

ggplot(data = google_mobility_data %>% filter(country_code %in% country_codes), aes(x = date, y = average_mobility, colour = country)) + geom_line() + 
    scale_color_hue(l = 50) + theme_bw()

7 Serial Interval

To do further analysis an serial interval assumption is needed. The serial interval is taken from [7]. It’s assumed to be Gamma distributed with mean of 6.48 and standard deviation of 3.83. This corresponds to the effective infectiousness of an individual since acquiring the infection themselves.

We plot this serial distribution below:

si_mean <- 6.48
si_sd <- 3.83
si_cv <- si_sd/si_mean

serial_interval = rep(0, 1)
serial_interval[1] = (pgammaAlt(1.5, si_mean, si_cv) - pgammaAlt(0, si_mean, si_cv))
for (i in 2:50) {
    serial_interval[i] = (pgammaAlt(i + 0.5, si_mean, si_cv) - pgammaAlt(i - 0.5, si_mean, si_cv))
}
ggplot(data.frame(days = seq(1, 20, 1), serial_interval = serial_interval[1:20]), aes(y = serial_interval, x = days)) + geom_line(size = 1) + theme_bw() + 
    theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position = "right") + guides(fill = guide_legend(ncol = 1)) + scale_color_hue(l = 50)

8 Estimate \(R_{t,m}\) values by week

8.1 Estimation Routine

The following code works backwards and fits \(R_{t,m}\) values for whole weeks (ending on the last date in the data) using the EpiEstim package. Using deaths or infection the as the cases in the puts the estimate of \(R_{t,m}\) as at the date of the cases being tracked.

So \(t\) is based on the date of reporting (be that cases or deaths) and \(m\) is the country or province. Two values are estimated for each country:

  1. \(R_{t,m}^{cases}\) is the reproductive number implied by the cases reported at time \(t\) in country \(m\).
  2. \(R_{t,m}^{deaths}\) is the reproductive number implied by the deaths reported at time \(t\) in country \(m\).

Note that the time periods are left unadjusted, though in reality the \(R_{t,m}^{deaths}\) should be shifted back approximately 2 weeks relative to \(R_{t,m}^{cases}\).

Rt_data <- NULL
for (c in levels(data$country)) {
    for (t in levels(data$type)) {
        # filter out country data of type t
        c_data <- data %>% filter(country == c & type == t)
        
        # vector of count of cases/deaths
        I <- c_data$count
        
        # t=1 corresponds to this date:
        t1_date <- min(c_data$date)
        
        # the day after the first case/death:
        t_start_date <- min((c_data %>% filter(count > 0))$date) + 1
        t_start <- min(seq(1, length(I))[I > 0]) + 1
        
        # last day of cases/deaths
        t_end <- length(I)
        t_end_date <- t1_date + (t_end - 1)
        
        # how many full weeks do we have
        full_weeks <- floor((t_end - t_start)/7)
        
        # only continue if we have 1 full week or more
        if (full_weeks > 0) {
            # then divide period into weeks
            T.Start <- seq(t_end - 7 * full_weeks, t_end - 7, by = 7)
            T.End <- T.Start + 7
            
            # estimate $R_{t,m}^{type}$ for each week:
            c_Rt <- EstimateR(I, T.Start = T.Start, T.End = T.End, Mean.SI = si_mean, Std.SI = si_sd, method = "ParametricSI")$R
            
            # count cases
            c_data$week <- floor(as.numeric(c_data$date - (t1_date + T.Start[1]))/7)
            c_data$dd <- c(0, c_data$date[2:nrow(c_data)] - c_data$date[1:(nrow(c_data) - 1)])
            c_agg_data <- c_data %>% filter(week >= 0) %>% group_by(week) %>% summarise(count = sum(count)) %>% ungroup()
            
            # add country and type designations
            c_Rt <- cbind(c_data$continent[1], c_data$country_code[1], c, t, c_Rt, c_agg_data$count)
            
            # and add dates
            c_Rt$date_start <- t1_date + c_Rt$T.Start
            c_Rt$date_end <- t1_date + c_Rt$T.End - 1
            
            # combine the results
            if (is.null(Rt_data)) {
                Rt_data <- c_Rt
            } else {
                Rt_data <- rbind(Rt_data, c_Rt)
            }
        }
    }
}

8.2 Prepping data for tabulation and plots

Below we prep column headings and prepare CI data:

# Column names
colnames(Rt_data) <- c("continent", "country_code", "country", "type", "t_start", "t_end", "Rt_mean", "Rt_std", "Rt_li_95", "Rt_li_90", "Rt_li_50", "Rt_median", 
    "Rt_ui_50", "Rt_ui_90", "Rt_ui_95", "count", "date_start", "date_end")

# mid week data point
Rt_data$date_mid <- Rt_data$date_start + (Rt_data$date_end - Rt_data$date_start)/2

# split out CI data into different dataset
Rt_ci_data <- NULL
for (ci in c("50", "90", "95")) {
    r <- data.frame(country_code = Rt_data$country_code, country = Rt_data$country, type = Rt_data$type, ci = rep(paste0(ci, "%"), nrow(Rt_data)), date_mid = Rt_data$date_mid, 
        Rt_mean = Rt_data$Rt_mean, Rt_li = Rt_data[[paste0("Rt_li_", ci)]], Rt_ui = Rt_data[[paste0("Rt_ui_", ci)]])
    Rt_ci_data <- rbind(r, Rt_ci_data)
}

# remove what is not needed
rm(list = ls()[!ls() %in% c("data", "country_codes", "Rt_data", "Rt_ci_data", "google_mobility_data")])

9 Linking \(R_{t,m}\) estimates to mobility

9.1 Preparing Rt_data

Linking the mobility data should be done in a way that corresponds to the time the infection should have occurred not the time of death. Below new date ranges are calculated to correspond roughly to the time of infection:

Rt_data$date_inf_start <- Rt_data$date_start - ifelse(Rt_data$type == "death", 23 + 7, 6 + 7)
Rt_data$date_inf_end <- Rt_data$date_end - ifelse(Rt_data$type == "death", 23 + 7, 6 + 7)
Rt_data$date_inf_mid <- Rt_data$date_mid - ifelse(Rt_data$type == "death", 23 + 7, 6 + 7)

9.2 Calculating weekly mobility data

The code below calculates average weekly mobility data averaged over the same date ranges as calculated above.

weekly_mobility_data <- NULL
combined_country_codes <- levels(Rt_data$country_code)[levels(Rt_data$country_code) %in% levels(google_mobility_data$country_code)]
for (c in combined_country_codes) {
    for (t in levels(Rt_data$type)) {
        c_data <- Rt_data %>% filter(country_code == c & type == t)
        if (nrow(c_data) > 0) {
            for (i in 1:nrow(c_data)) {
                w_data <- google_mobility_data %>% filter(country_code == c & date >= c_data$date_inf_start[i] & date <= c_data$date_inf_end[i]) %>% 
                  group_by(country_code) %>% summarise(retail_and_recreation = mean(retail_and_recreation), grocery_and_pharmacy = mean(grocery_and_pharmacy), 
                  parks = mean(parks), transit_stations = mean(transit_stations), workplaces = mean(workplaces), residential = mean(residential), average_mobility = mean(average_mobility)) %>% 
                  ungroup()
                if (!is.na(w_data$country_code)) {
                  w_data$type <- t
                  w_data$date_inf_mid <- c_data$date_inf_mid[i]
                  weekly_mobility_data <- rbind(weekly_mobility_data, w_data)
                }
            }
        }
    }
}

9.3 Joining with \(R_{t,m}\) estimates

Based on work above the weekly mobility data can be joined:

Rt_data <- left_join(Rt_data, weekly_mobility_data, by = c("country_code", "type", "date_inf_mid"))

10 Modelling \(R_{t,m}\) as a function of Mobility Indexes

TO DO

11 Results

11.1 Current \(R_{t,m}\) estimates

Below current (last weekly) \(R_{t,m}\) estimates are tabulated. To so the data first needs to be prepared:

# find the last estimates
last_dates <- Rt_data %>% group_by(country, type) %>% summarise(date_mid = max(date_mid)) %>% ungroup()

# construct a table with nice fields names
table <- inner_join(last_dates, Rt_data, by = c("country", "type", "date_mid"))
table <- table %>% select(continent, country_code, country, type, count, date_end, Rt_li_95, Rt_mean, Rt_ui_95)

11.1.1 South Africa

knitr::kable((table %>% filter(continent == "SA"))[, c(-1, -2)], format.args = list(big.mark = ","), digits = 1, col.names = c("Country", "Estimated Type", 
    "Count (Last Week)", "Week Ending", "R - Lower CI", "R - Mean", "R - Uppper CI"))
Country Estimated Type Count (Last Week) Week Ending R - Lower CI R - Mean R - Uppper CI
EC cases 3,629 2020-06-13 1.7 1.8 1.8
EC deaths 116 2020-06-13 2.5 3.0 3.5
FS cases 109 2020-06-13 1.0 1.2 1.5
FS deaths 0 2020-06-13 0.0 0.7 2.6
GP cases 4,279 2020-06-13 2.1 2.1 2.2
GP deaths 34 2020-06-13 2.0 2.8 3.7
KZN cases 751 2020-06-13 1.2 1.2 1.3
KZN deaths 3 2020-06-13 0.6 1.2 2.0
LP cases 93 2020-06-13 1.2 1.5 1.9
LP deaths 1 2020-06-13 1.1 9.2 25.6
MP cases 96 2020-06-13 1.4 1.7 2.1
NC cases 39 2020-06-13 0.7 1.0 1.3
NC deaths 0 2020-06-13 0.1 4.6 17.2
NW cases 510 2020-06-13 1.6 1.7 1.9
NW deaths 4 2020-06-13 2.7 8.2 16.7
SA cases 19,762 2020-06-13 1.3 1.3 1.3
SA deaths 471 2020-06-13 1.3 1.4 1.5
WC cases 10,262 2020-06-13 1.0 1.1 1.1
WC deaths 312 2020-06-13 1.0 1.1 1.2

11.1.2 Other Countries

The table below contains \(R_{t,m}\) estimates for the last week available. These estimates are based either on cases or deaths and a 95% confidence interval is shown.

knitr::kable((table %>% filter(continent != "SA" & country_code %in% country_codes))[, c(-1, -2)], format.args = list(big.mark = ","), digits = 1, col.names = c("Country", 
    "Estimated Type", "Count (Last Week)", "Week Ending", "R - Lower CI", "R - Mean", "R - Uppper CI"))
Country Estimated Type Count (Last Week) Week Ending R - Lower CI R - Mean R - Uppper CI
Brazil cases 177,677 2020-06-14 1.0 1.0 1.0
Brazil deaths 6,790 2020-06-14 0.9 1.0 1.0
Canada cases 3,353 2020-06-14 0.7 0.7 0.8
Canada deaths 334 2020-06-14 0.5 0.6 0.7
Chile cases 39,610 2020-06-14 1.2 1.2 1.2
Chile deaths 1,560 2020-06-14 1.7 1.8 1.8
India cases 74,294 2020-06-14 1.2 1.2 1.2
India deaths 2,266 2020-06-14 1.2 1.3 1.3
Ireland cases 112 2020-06-14 0.4 0.5 0.6
Ireland deaths 35 2020-06-14 0.7 0.9 1.3
Italy cases 1,850 2020-06-14 0.8 0.8 0.8
Italy deaths 455 2020-06-14 0.8 0.9 0.9
Peru cases 33,374 2020-06-14 0.9 0.9 0.9
Peru deaths 1,197 2020-06-14 1.1 1.2 1.3
South_Africa cases 19,763 2020-06-14 1.3 1.3 1.3
South_Africa deaths 471 2020-06-14 1.3 1.4 1.5
Spain cases 2,295 2020-06-13 0.9 0.9 0.9
Spain deaths 1 2020-06-13 0.0 0.0 0.1
United_Kingdom cases 9,507 2020-06-14 0.8 0.8 0.8
United_Kingdom deaths 1,197 2020-06-14 0.6 0.7 0.7

11.1.3 Top 10 Countries

Below we show various extremes of \(R_{t,m}\) where counts (deaths or cases) exceed 50 in the last week.

11.1.3.1 Lowest \(R_{t,m}\) based on Deaths

knitr::kable((table %>% filter(continent != "SA" & type == "deaths" & count > 50) %>% arrange(Rt_mean))[1:10, c(-1, -2)], format.args = list(big.mark = ","), 
    digits = 1, col.names = c("Country", "Estimated Type", "Count (Last Week)", "Week Ending", "R - Lower CI", "R - Mean", "R - Uppper CI"))
Country Estimated Type Count (Last Week) Week Ending R - Lower CI R - Mean R - Uppper CI
Belgium deaths 70 2020-06-14 0.4 0.6 0.7
Canada deaths 334 2020-06-14 0.5 0.6 0.7
France deaths 256 2020-06-14 0.6 0.7 0.7
United_Kingdom deaths 1,197 2020-06-14 0.6 0.7 0.7
Germany deaths 119 2020-06-14 0.6 0.7 0.8
Sweden deaths 218 2020-06-14 0.6 0.7 0.8
Turkey deaths 123 2020-06-14 0.7 0.8 0.9
Italy deaths 455 2020-06-14 0.8 0.9 0.9
United_States_of_America deaths 5,634 2020-06-14 0.9 0.9 0.9
Mexico deaths 3,361 2020-06-14 0.9 0.9 1.0

11.1.3.2 Lowest \(R_{t,m}\) based on Cases

knitr::kable((table %>% filter(continent != "SA" & type == "cases" & count > 50) %>% arrange(Rt_mean))[1:10, c(-1, -2)], format.args = list(big.mark = ","), 
    digits = 1, col.names = c("Country", "Estimated Type", "Count (Last Week)", "Week Ending", "R - Lower CI", "R - Mean", "R - Uppper CI"))
Country Estimated Type Count (Last Week) Week Ending R - Lower CI R - Mean R - Uppper CI
Sri_Lanka cases 70 2020-06-14 0.3 0.3 0.4
Djibouti cases 280 2020-06-14 0.3 0.3 0.4
Malaysia cases 142 2020-06-14 0.3 0.4 0.4
Ireland cases 112 2020-06-14 0.4 0.5 0.6
Maldives cases 112 2020-06-14 0.5 0.5 0.6
Gabon cases 362 2020-06-14 0.5 0.6 0.7
Uganda cases 101 2020-06-14 0.6 0.7 0.8
Tajikistan cases 518 2020-06-14 0.6 0.7 0.7
Canada cases 3,353 2020-06-14 0.7 0.7 0.8
Malawi cases 120 2020-06-14 0.6 0.8 0.9

11.1.3.3 Highest \(R_{t,m}\) based on Deaths

knitr::kable((table %>% filter(continent != "SA" & type == "deaths" & count > 50) %>% arrange(-Rt_mean))[1:10, c(-1, -2)], format.args = list(big.mark = ","), 
    digits = 1, col.names = c("Country", "Estimated Type", "Count (Last Week)", "Week Ending", "R - Lower CI", "R - Mean", "R - Uppper CI"))
Country Estimated Type Count (Last Week) Week Ending R - Lower CI R - Mean R - Uppper CI
Cameroon deaths 72 2020-06-14 2.8 3.6 4.5
Iraq deaths 231 2020-06-14 1.6 1.9 2.1
Chile deaths 1,560 2020-06-14 1.7 1.8 1.8
Afghanistan deaths 124 2020-06-14 1.2 1.4 1.7
Moldova deaths 67 2020-06-14 1.1 1.4 1.7
South_Africa deaths 471 2020-06-14 1.3 1.4 1.5
Philippines deaths 80 2020-06-14 1.1 1.4 1.7
Argentina deaths 167 2020-06-14 1.1 1.3 1.5
Saudi_Arabia deaths 256 2020-06-14 1.2 1.3 1.5
Colombia deaths 387 2020-06-14 1.2 1.3 1.4

11.1.3.4 Highest \(R_{t,m}\) based on Cases

knitr::kable((table %>% filter(continent != "SA" & type == "cases" & count > 50) %>% arrange(-Rt_mean))[1:10, c(-1, -2)], format.args = list(big.mark = ","), 
    digits = 1, col.names = c("Country", "Estimated Type", "Count (Last Week)", "Week Ending", "R - Lower CI", "R - Mean", "R - Uppper CI"))
Country Estimated Type Count (Last Week) Week Ending R - Lower CI R - Mean R - Uppper CI
Angola cases 52 2020-06-14 2.5 3.3 4.3
Eswatini cases 164 2020-06-14 2.3 2.7 3.1
Benin cases 144 2020-06-14 2.2 2.6 3.0
Bulgaria cases 555 2020-06-14 2.0 2.2 2.4
Kosovo cases 295 2020-06-14 1.9 2.1 2.4
Jordan cases 158 2020-06-14 1.8 2.1 2.4
China cases 102 2020-06-14 1.7 2.0 2.4
Greece cases 160 2020-06-14 1.7 1.9 2.2
Zambia cases 203 2020-06-14 1.7 1.9 2.1
Sao_Tome_and_Principe cases 146 2020-06-14 1.5 1.8 2.1

11.2 Result Plots

Let’s generate our plots for each country/province in a list. We filter out weeks where the upper end of confidence interval for $R_{t,m} exceeds five.

country_plots <- list()
for (c in levels(Rt_data$country)) {
    p1 <- ggplot(Rt_ci_data %>% filter(country == c & ci == "95%" & Rt_ui <= 5), aes(x = date_mid, y = Rt_mean)) + geom_crossbar(position = position_dodge2(padding = 0), 
        aes(ymin = Rt_li, ymax = Rt_ui, colour = type, fill = type), width = 7) + scale_fill_manual(values = c(alpha("deepskyblue4", 0.45), alpha("#8b0068", 
        0.45))) + scale_colour_manual(values = c(alpha("deepskyblue4"), alpha("#8b0068"))) + scale_y_continuous(limits = c(0, 5)) + scale_x_date(date_breaks = "1 months", 
        labels = date_format("%e %b")) + theme_bw() + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position = "right") + guides(fill = guide_legend(ncol = 1)) + 
        xlab("Week") + ylab(expression(R[t, m])) + ggtitle(c) + geom_hline(yintercept = 1)
    country_plots[[c]] <- p1
}

11.2.1 South Africa

Below we plot all the provinces:

ggarrange(country_plots[["EC"]], country_plots[["FS"]], country_plots[["GP"]], country_plots[["KZN"]], country_plots[["LP"]], country_plots[["MP"]], 
    country_plots[["NC"]], country_plots[["NW"]], country_plots[["WC"]], country_plots[["SA"]], ncol = 2, nrow = 5) + ggtitle("Reproductive number by province, with 95% confidence intervals")

11.2.2 Other Countries

Below we plot all the countries:

ggarrange(country_plots[["Brazil"]], country_plots[["Canada"]], country_plots[["Chile"]], country_plots[["India"]], country_plots[["Ireland"]], country_plots[["Italy"]], 
    country_plots[["Peru"]], country_plots[["South_Africa"]], country_plots[["Spain"]], country_plots[["United_Kingdom"]], ncol = 2, nrow = 5) + ggtitle("Reproductive number by country, with 95% confidence intervals")

11.2.3 Scatter Plots of \(R_{t,m}^{cases}\) vs. \(R_{t,m}^{deaths}\)

Below we plot estimates of \(R_{t,m}\) based on cases versus those based on deaths for countries where these estimates range between 0.5 and 2.

11.2.3.1 South Africa

Below we plot where there were more than 5 deaths and cases in the last week. It appears that, generally, \(R_{t,m}^{cases}\) and \(R_{t,m}^{deaths}\) are consistent.

sp_data <- pivot_wider(table %>% filter(count >= 5 & continent == "SA"), id_cols = c("continent", "country"), names_from = "type", values_from = "Rt_mean")

ggplot(sp_data, aes(x = cases, y = deaths)) + geom_point(aes(label = country)) + geom_text(aes(label = country), hjust = 0, vjust = 0, size = 2) + ggtitle("Reproductive number by country") + 
    xlab(expression(R[t, m]^cases)) + ylab(expression(R[t, m]^deaths)) + scale_y_continuous(limits = c(0.5, 2)) + scale_x_continuous(limits = c(0.5, 
    2)) + theme_bw() + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position = "right") + guides(fill = guide_legend(ncol = 1)) + 
    scale_color_hue(l = 50) + geom_abline(a = 0, b = 1)

11.2.3.2 Other countries

Below we plot where there were more than 25 deaths and cases in the last week. It appears that, generally, \(R_{t,m}^{cases}\) and \(R_{t,m}^{deaths}\) are consistent.

sp_data <- pivot_wider(table %>% filter(count >= 25 & continent != "SA"), id_cols = c("continent", "country"), names_from = "type", values_from = "Rt_mean")

ggplot(sp_data, aes(x = cases, y = deaths)) + geom_point(aes(color = continent, label = country)) + geom_text(aes(label = country), hjust = 0, vjust = 0, 
    size = 2) + ggtitle("Reproductive number by country") + xlab(expression(R[t, m]^cases)) + ylab(expression(R[t, m]^deaths)) + scale_y_continuous(limits = c(0.5, 
    2)) + scale_x_continuous(limits = c(0.5, 2)) + theme_bw() + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position = "right") + 
    guides(fill = guide_legend(ncol = 1)) + scale_color_hue(l = 50) + geom_abline(a = 0, b = 1)

11.3 Mobility

11.3.1 Simple plot

Below we plot weeks of estimates of \(R_{t,m}\) versus the average mobility index that was constructed. There is a clear link between mobility and the reproductive number. As it decreases \(R_{t,m}\) reduces:

ggplot(Rt_data %>% filter(type == "cases" & count > 100 & country_code %in% c(country_codes)), aes(x = average_mobility, y = Rt_mean)) + geom_point(aes(colour = country)) + 
    ggtitle("Reproductive number by mobility") + ylab(expression(R[t, m]^cases)) + # scale_y_continuous(limits = c(0, 10)) +
theme_bw() + theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position = "right") + guides(fill = guide_legend(ncol = 1)) + scale_color_hue(l = 50)

11.3.2 Modelling Results

TO DO

12 Discussion

From the basic plots it’s clear that South Africa and a few other countries appear to be on a different “slope” than European countries show. These include Brazil, Peru, Chile and India.

The above shows estimates for reproductive number using cases and deaths (\(R_{t,m}^{cases}\) and \(R_{t,m}^{deaths}\)) for each country \(m\) over time \(t\) in weeks.

From the current estimates it appears that, at present, in general South African provincial estimates based on cases and deaths correspond. An exception to this may be Eastern Cape with estimates based on deaths exceeding those based on cases.

It is also clear that estimates based on cases appear to precede the movements based on deaths over time. This can be expected as, all things being equal, infections and associated cases should be a precursor to deaths.

South Africa does not compare well, and appears to have one of the highest \(R_{t,m}^{deaths}\) in the world. This is higher than the \(R_{t,m}^{cases}\) which may be indicative of lower testing and/or backlogs with regard to testing.

On a scatter plot of countries most appear to have \(R_{t,m}^{cases}\) correlated well with \(R_{t,m}^{deaths}\). Chile, Afghanistan and Iran appear to be outliers with $R_{t,m}^{deaths} higher than \(R_{t,m}^{cases}\) indicating perhaps limited testing, or alternatively epidemics that are slowing (given the leading nature of cases vs. deaths).

Overall it’s clear that having multiple measures of \(R_{t,m}\) appears useful and monitoring it’s value is something useful.

13 Detailed Output

Detailed output for all countries are saved to a comma-separated value file below. The file can be found here.

write.csv(Rt_data, "Rt_data.csv", row.names = FALSE)

14 Author

This report was prepared by Louis Rossouw. Please get in contact with Louis Rossouw if you have comments or wish to receive this regularly.

Louis Rossouw
Head of Research & Analytics
Gen Re | Life/Health Canada, South Africa, Australia, NZ, UK & Ireland
Email: LRossouw@GenRe.com Mobile: +27 71 355 2550

The views in this document represents that of the author and may not represent those of Gen Re. Also note that given the significant uncertainty involved with the parameters, data and methodology care should be taken with these numbers and any use of these numbers.

15 References

[1] A. Cori, N. M. Ferguson, C. Fraser, and S. Cauchemez, “A new framework and software to estimate time-varying reproduction numbers during epidemics,” American Journal of Epidemiology, vol. 178, no. 9, pp. 1505–1512, Sep. 2013, doi: 10.1093/aje/kwt133. [Online]. Available: https://doi.org/10.1093/aje/kwt133

[2] A. Cori, EpiEstim: EpiEstim: A package to estimate time varying reproduction numbers from epidemic curves. 2013 [Online]. Available: https://CRAN.R-project.org/package=EpiEstim

[3] V. Marivate et al., “Coronavirus disease (COVID-19) case data - South Africa.” Zenodo, 2020 [Online]. Available: https://zenodo.org/record/3888499

[4] European Centre for Disease Prevention and Control, “Data on the geographic distribution of COVID-19 cases worldwide.” European Union, 2020 [Online]. Available: https://www.ecdc.europa.eu/en/publications-data/download-todays-data-geographic-distribution-covid-19-cases-worldwide

[5] Google LLC, “Google COVID-19 community mobility reports.” 2020 [Online]. Available: https://www.google.com/covid19/mobility/

[6] P. Nouvellet et al., “Report 26: Reduction in mobility and covid-19 transmission,” Imperial College London, 2020 [Online]. Available: http://spiral.imperial.ac.uk/handle/10044/1/79643

[7] N. Ferguson et al., “Report 9: Impact of non-pharmaceutical interventions (NPIs) to reduce COVID19 mortality and healthcare demand,” Imperial College London, 2020 [Online]. Available: https://www.imperial.ac.uk/mrc-global-infectious-disease-analysis/covid-19/report-9-impact-of-npis-on-covid-19/

LS0tDQp0aXRsZTogIkVzdGltYXRpbmcgdGhlIFJlcHJvZHVjdGl2ZSBOdW1iZXIgb2YgQ09WSUQtMTkiDQphdXRob3I6ICJMb3VpcyBSb3Nzb3V3Ig0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIGluY2x1ZGVzOg0KICAgICAgaW5faGVhZGVyOiBnb29nbGVfYW5hbHl0aWNzX2hlYWRlci5odG1sDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICBpbmNsdWRlczoNCiAgICAgIGluX2hlYWRlcjogZ29vZ2xlX2FuYWx5dGljc19oZWFkZXIuaHRtbA0KY3NsOiBpZWVlX3dpdGhfdXJsLmNzbA0KYmlibGlvZ3JhcGh5OiByZWZlcmVuY2VzLmJpYg0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Kcm0obGlzdD1scygpKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG89VFJVRSwgd2FybmluZyA9IEZBTFNFLCB0aWR5PVRSVUUsIGRwaT0zMDApDQpgYGANCg0KKioqVEhJUyBXT1JLIElTIFlFVCBUTyBCRSBSRVZJRVdFRCoqKg0KDQojIEludHJvZHVjdGlvbg0KDQpUaGlzIHBhcGVyIGNvbnRhaW5zIGVzdGltYXRlcyBmb3IgdGhlIHJlcHJvZHVjdGl2ZSBudW1iZXIgJFJfe3QsbX0kIG92ZXIgdGltZSAkdCQgaW4gdmFyaW91cyBjb3VudHJpZXMgJG0kLiAgSXQgYWxzbyBzaG93cyB0aGUgc2FtZSBmb3IgcHJvdmluY2VzIHdpdGhpbiBTb3V0aCBBZnJpY2EuLiAgVGhpcyBpcyBkb25lIHVzaW5nIHRoZSBtZXRob2RvbG9neSBhcyBkZXNjcmliZWQgaW4gW0BDb3JpMjAxM10uICBUaGVzZSBoYXZlIGJlZW4gaW1wbGVtZW50ZWQgaW4gUiB1c2luZyBgRXBpRXN0aW1gIHBhY2thZ2UgW0BDb3JpMjAxM2FdIHdoaWNoIGlzIHdoYXQgaXMgdXNlZCBoZXJlLiAgDQoNClRoaXMgcGFwZXIgYW5kIGl0J3MgcmVzdWx0cyBzaG91bGQgYmUgdXBkYXRlZCByb3VnaGx5IGRhaWx5IGFuZCBbaXMgYXZhaWxhYmxlIG9ubGluZV0oaHR0cHM6Ly9scm9zc291dy5naXRodWIuaW8vY292aWQtMTkvZXN0aW1hdGluZ19yLm5iLmh0bWw/dXRtX3NvdXJjZT1wYXBlciZ1dG1fbWVkaXVtPXBhcGVyX3VybCZ1dG1fY2FtcGFpZ249d2VlazIpLg0KDQojIFVwZGF0ZXMNCg0KQXMgdGhpcyBwYXBlciBpcyB1cGRhdGVkIG92ZXIgdGltZSB0aGlzIHNlY3Rpb24gd2lsbCBzdW1tYXJpc2Ugc2lnbmlmaWNhbnQgY2hhbmdlcy4gIFRoZSBjb2RlIHByb2R1Y2luZyB0aGlzIHBhcGVyIGlzIHRyYWNrZWQgdXNpbmcgR2l0LiBUaGUgR2l0IGNvbW1pdCBoYXNoIGZvciB0aGlzIHByb2plY3QgYXQgdGhlIHRpbWUgb2YgZ2VuZXJhdGluZyB0aGlzIHBhcGVyIHdhcyBgciBzeXN0ZW0oImdpdCByZXYtcGFyc2UgSEVBRCIsIGludGVybj1UUlVFKWAuDQoNCioqMjAyMC0wNi0xMioqDQoNCiogSW5pdGlhbCB2ZXJzaW9uLg0KDQoqKjIwMjAtMDYtMTMqKg0KDQoqIENvbWJpbmUgdGhlIHZlcnNpb24gZm9yIFNvdXRoIEFmcmljYSBhbmQgb3RoZXIgY291bnRyaWVzLg0KDQoNCiMgTGlicmFyaWVzDQoNClRoZSBwcm9qZWN0IHVzZXMgdGhlIGZvbGxvd2luZyBsaWJyYXJpZXMuDQoNCmBgYHtyIGxpYnJhcmllcywgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCnJlcXVpcmUoRXBpRXN0aW0pDQpyZXF1aXJlKEVudlN0YXRzKQ0KcmVxdWlyZShnZ3Bsb3QyKQ0KcmVxdWlyZShnZ3B1YnIpDQpyZXF1aXJlKGx1YnJpZGF0ZSkNCnJlcXVpcmUodXRpbHMpDQpyZXF1aXJlKGh0dHIpDQpyZXF1aXJlKGRwbHlyKQ0KcmVxdWlyZSh0aWR5cikNCnJlcXVpcmUoc2NhbGVzKQ0KYGBgDQoNCiMgQ291bnRyaWVzDQoNCkFsbCBjb3VudHJpZXMgYXZhaWxhYmxlIGluIHRoZSBkYXRhIHdpbGwgYmUgYW5hbHlzZWQgYnV0IGFsc28gdGhlIHByb3ZpbmNlcyBvZiBTb3V0aCBBZnJpY2EuICBUaGlzIHBhcGVyIHByb2R1Y2VzIGNoYXJ0cyBmb3IgdGhlIGZvbGxvd2luZyBjb3VudHJpZXMgb25seToNCmBgYHtyIGNvdW50cmllc30NCmNvdW50cnlfY29kZXMgPSBjKCJCUiIsICMgQnJhemlsDQogICAgICAgICAgICAgICJDQSIsICMgQ2FuYWRhDQogICAgICAgICAgICAgICJDTCIsICMgQ2hpbGUNCiAgICAgICAgICAgICAgIklOIiwgIyBJbmRpYQ0KICAgICAgICAgICAgICAiSUUiLCAjIElyZWxhbmQNCiAgICAgICAgICAgICAgIklUIiwgIyBJdGFseQ0KICAgICAgICAgICAgICAiUEUiLCAjIFBlcnUNCiAgICAgICAgICAgICAgIlpBIiwgIyBTb3V0aCBBZnJpY2ENCiAgICAgICAgICAgICAgIkVTIiwgIyBTcGFpbg0KICAgICAgICAgICAgICAiVUsiICMgVW5pdGVkIEtpbmdkb20NCiAgICAgICAgICAgICAgKQ0KYGBgDQoNCiMgRGF0YQ0KDQojIyBTb3V0aCBBZnJpY2ENCg0KIyMjIERvd25sb2FkDQpEYXRhIGlzIGRvd25sb2FkZWQgZnJvbSB0aGUgR2l0IHJlcG9zaXRvcnkgYXNzb2NpYXRlZCB3aXRoIFtATWFyaXZhdGUyMDIwXS4NCg0KYGBge3IgZG93bmxvYWRfZGF0YV96YSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCiMgUHJvdmluY2lhbCBEZWF0aHMNCkdFVCgNCiAgdXJsID0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9kc2ZzaS9jb3ZpZDE5emEvbWFzdGVyL2RhdGEvY292aWQxOXphX3Byb3ZpbmNpYWxfY3VtdWxhdGl2ZV90aW1lbGluZV9kZWF0aHMuY3N2IiwNCiAgd3JpdGVfZGlzaygNCiAgICAiY292aWQxOXphX3Byb3ZpbmNpYWxfY3VtdWxhdGl2ZV90aW1lbGluZV9kZWF0aHMuY3N2IiwNCiAgICBvdmVyd3JpdGUgPSBUUlVFDQogICkNCikNCg0KIyBQcm92aW5jaWFsIENhc2VzDQpHRVQoDQogIHVybCA9ICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZHNmc2kvY292aWQxOXphL21hc3Rlci9kYXRhL2NvdmlkMTl6YV9wcm92aW5jaWFsX2N1bXVsYXRpdmVfdGltZWxpbmVfY29uZmlybWVkLmNzdiIsDQogIHdyaXRlX2Rpc2soDQogICAgImNvdmlkMTl6YV9wcm92aW5jaWFsX2N1bXVsYXRpdmVfdGltZWxpbmVfY29uZmlybWVkLmNzdiIsDQogICAgb3ZlcndyaXRlID0gVFJVRQ0KICApDQopDQpgYGANCg0KIyMjIERhdGEgcHJlcGVyYXRpb24NCg0KRmlyc3QgcmVhZCBpbiB0aGUgZGF0YSBmcm9tIHRoZSBkb3dubG9hZGVkIGNvbW1hLXNlcGFyYXRlZCB2YWx1ZXMgdGV4dCBmaWxlLg0KDQpgYGB7ciByZWFkX2RhdGFfemF9DQojIFJlYWQgZnJvbSBDU1ZzIGFib3ZlDQpkYXRhX3phX2Nhc2VzIDwtDQogIHJlYWQuY3N2KA0KICAgICJjb3ZpZDE5emFfcHJvdmluY2lhbF9jdW11bGF0aXZlX3RpbWVsaW5lX2NvbmZpcm1lZC5jc3YiLA0KICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICApDQpkYXRhX3phX2RlYXRocyA8LQ0KICByZWFkLmNzdigiY292aWQxOXphX3Byb3ZpbmNpYWxfY3VtdWxhdGl2ZV90aW1lbGluZV9kZWF0aHMuY3N2IiwNCiAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KYGBgDQoNCkluIHRoZSBjYXNlIGRhdGEgZmlsZSByb3cgMjEgYW5kIDMyIGNvbnRhaW4gbm8gcHJvdmluY2lhbCBkZXRhaWxzLiBXZSBlc3RpbWF0ZWQgaXQgYnkgc3ByZWFkaW5nIHRoZSBuYXRpb25hbCB0b3RhbCB0byB0aGUgcHJvdmluY2VzIGluIHByb3BvcnRpb24gdG8gYSBtaXh0dXJlIG9mIHRoZSBwcmlvciBkYXkgYW5kIHRoZSBuZXh0IGRheS4NCg0KYGBge3IgZml4X3phX2Nhc2VzfQ0KZGF0YV96YV9jYXNlc1syMSwgYygiRUMiLCAiRlMiLCAiR1AiLCAiS1pOIiwgIkxQIiwgIk1QIiwgIk5DIiwgIk5XIiwgIldDIiwgIlVOS05PV04iKV0gPC0NCiAgY29sU3VtcyhkYXRhX3phX2Nhc2VzW2MoMjAsIDIyKSwgYygiRUMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRlMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR1AiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiS1pOIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxQIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1QIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5DIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5XIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIldDIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlVOS05PV04iKV0pIC8gc3VtKGRhdGFfemFfY2FzZXNbYygyMCwgMjIpLCBdJHRvdGFsKSAqIGRhdGFfemFfY2FzZXNbMjEsIF0kdG90YWwNCmRhdGFfemFfY2FzZXNbMzIsIGMoIkVDIiwgIkZTIiwgIkdQIiwgIktaTiIsICJMUCIsICJNUCIsICJOQyIsICJOVyIsICJXQyIsICJVTktOT1dOIildIDwtDQogIGNvbFN1bXMoZGF0YV96YV9jYXNlc1tjKDMxLCAzMyksIGMoIkVDIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZTIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdQIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktaTiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMUCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNUCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOQyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOVyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJXQyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJVTktOT1dOIildKSAvIHN1bShkYXRhX3phX2Nhc2VzW2MoMzEsIDMzKSwgXSR0b3RhbCkgKiBkYXRhX3phX2Nhc2VzWzMyLCBdJHRvdGFsDQoNCmBgYA0KDQpUaGUgZm9sbG93aW5nIGZ1bmN0aW9uIHdpbGwgYmUgYXBwbGllZCB0byBjYXNlIGFuZCBkZWF0aCBkYXRhLiAgSW4gdGhpcyBmdW5jdGlvbiB0aGUgZm9sbG93aW5nIG9jY3VyczoNCg0KMS4gU2NhbGUgdXAgdGhlIHBlciBwcm92aW5jZSBkYXRhIGZvciB1bmtub3duIHZhbHVlcy4NCjIuIFRoaXMgcmVzdWx0cyBpbiBwcm92aW5jaWFsIGRhdGEgd2hpY2ggYXJlIG5vdCB3aG9sZSBudW1iZXJzLiAgVGhlc2UgYXJlIHJvdW5kZWQgdG8gdGhlIG5lYXJlc3Qgd2hvbGUgbnVtYmVyLg0KMy4gQSBgU0FgIGNvbHVtbiBpcyBhZGRlZCBhcyB0aGUgc3VtIG9mIHRoZSBuZXcgcGVyIHByb3ZpbmNlIGRhdGEuDQo0LiBEYXRhIGlzIGZvcm1hdHRlZCBhbmQgZGlzYWdncmVnYXRlZCBzdWNoIHRoYXQgaXRlbSByZXByZXNlbnRzIHRoZSBpbmNyZW1lbnRhbCBjYXNlcyBvciBkZWF0aHMgcmF0aGVyIHRoYW4gY3VtdWxhdGl2ZSBmaWd1cmVzLg0KNS4gRGF0YSBpcyBmaWxsZWQgd2l0aCBkYXRhIChhbGJlaXQgd2l0aCAwIGNhc2VzIG9yIGRlYXRocykgZm9yIGFsbCBkYXRlcyBpbiB0aGUgcmFuZ2UuDQo2LiBBbnkgaW5jcmVtZW50YWwgY2FzZSBvciBkZWF0aCBjb3VudHMgdGhhdCBhcmUgbmVnYXRpdmUgYXJlIHplcm9laXNlZC4NCjYuIE5ldyBjdW11bGF0aXZlIGZpZ3VyZXMgYXJlIGNhbGN1bGF0ZWQuDQoNCg0KYGBge3IgZml4X2RhdGFfemF9DQpmaXhfZGF0YV96YSA8LQ0KICBmdW5jdGlvbihkYXRhX3phLA0KICAgICAgICAgICBzdGFydF9kYXRlID0gYXMuRGF0ZSgiMjAyMC0wMy0wMSIpLA0KICAgICAgICAgICBlbmRfZGF0ZSA9IGFzLkRhdGUoIjIwMjAtMDMtMzEiKSkgew0KICAgICMgU2NhbGUgcHJvdmluY2VzIGJ5IHNjYWxlIGZhY3RvciAoYXNzdW1lIHVua25vd24gYXJlIGluIHByb3BvcnRpb24pDQogICAgZGF0YV96YVssIGMoIkVDIiwgIkZTIiwgIkdQIiwgIktaTiIsICJMUCIsICJNUCIsICJOQyIsICJOVyIsICJXQyIpXSA8LQ0KICAgICAgZGF0YV96YVssIGMoIkVDIiwgIkZTIiwgIkdQIiwgIktaTiIsICJMUCIsICJNUCIsICJOQyIsICJOVyIsICJXQyIpXSAqDQogICAgICAoMSArIGRhdGFfemEkVU5LTk9XTiAvDQogICAgICAgICByb3dTdW1zKGRhdGFfemFbLCBjKCJFQyIsICJGUyIsICJHUCIsICJLWk4iLCAiTFAiLCAiTVAiLCAiTkMiLCAiTlciLCAiV0MiKV0pKQ0KICAgIA0KICAgICMgT25seSBzZWxlY3QgY29sdW1ucyB3ZSBuZWVkDQogICAgZGF0YV96YSA8LSBkYXRhX3phICU+JQ0KICAgICAgc2VsZWN0KCJkYXRlIiwgIkVDIiwgIkZTIiwgIkdQIiwgIktaTiIsICJMUCIsICJNUCIsICJOQyIsICJOVyIsICJXQyIpDQogICAgZGF0YV96YSRkYXRlIDwtIGFzLkRhdGUoZGF0YV96YSRkYXRlLCAiJWQtJW0tJVkiKQ0KICAgIA0KICAgICMgUm91bmQgZGF0YSBzbyB3ZSBoYXZlIGludGVnZXIgY2FzZXMNCiAgICBkYXRhX3phWywgYygiRUMiLCAiRlMiLCAiR1AiLCAiS1pOIiwgIkxQIiwgIk1QIiwgIk5DIiwgIk5XIiwgIldDIildIDwtDQogICAgICByb3VuZChkYXRhX3phWywgYygiRUMiLCAiRlMiLCAiR1AiLCAiS1pOIiwgIkxQIiwgIk1QIiwgIk5DIiwgIk5XIiwgIldDIildICwgMCkNCiAgICANCiAgICAjIENhbGN1bGF0ZSBhIG5ldyBTQSBjb2x1bW4NCiAgICBkYXRhX3phJFNBIDwtDQogICAgICByb3dTdW1zKGRhdGFfemFbLCBjKCJFQyIsICJGUyIsICJHUCIsICJLWk4iLCAiTFAiLCAiTVAiLCAiTkMiLCAiTlciLCAiV0MiKV0pDQogICAgDQogICAgIyAiTWVsdCIgdGhlIGRhdGENCiAgICBkYXRhX3phIDwtDQogICAgICBwaXZvdF9sb25nZXIoDQogICAgICAgIGRhdGFfemEsDQogICAgICAgIGNvbHMgPSBjKCJFQyIsICJGUyIsICJHUCIsICJLWk4iLCAiTFAiLCAiTVAiLCAiTkMiLCAiTlciLCAiV0MiLCAiU0EiKSwNCiAgICAgICAgbmFtZXNfdG8gPSAicHJvdmluY2UiLA0KICAgICAgICB2YWx1ZXNfdG8gPSAiY291bnQiDQogICAgICApDQogICAgDQogICAgIyBHZXR0aW5nIGRhaWx5IGRhdGEgZnJvbSB0aGUgY3VtdWxhdGl2ZSBkYXRhIHNldA0KICAgIGRhdGFfemEgPSAgZGF0YV96YSAlPiUNCiAgICAgIGdyb3VwX2J5KHByb3ZpbmNlKSAlPiUNCiAgICAgIGFycmFuZ2UoZGF0ZSkgJT4lDQogICAgICBtdXRhdGUoY291bnQgPSBjb3VudCAtIGxhZyhjb3VudCwgZGVmYXVsdCA9IDApKSAlPiUNCiAgICAgIHVuZ3JvdXAoKQ0KDQogICAgIyBhZGQgbWlzc2luZyBkYXRlcw0KICAgIGFsbF9kYXRlcyA8LSBleHBhbmRfZ3JpZChkYXRlID0gc2VxKHN0YXJ0X2RhdGUsIGVuZF9kYXRlLCAxKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvdmluY2UgPSBsZXZlbHMoYXMuZmFjdG9yKGRhdGFfemEkcHJvdmluY2UpKSkNCiAgICANCiAgICAjIGpvaW4NCiAgICBkYXRhX3phIDwtIGxlZnRfam9pbihhbGxfZGF0ZXMsIGRhdGFfemEsIGJ5ID1jKCJkYXRlIiwicHJvdmluY2UiKSkNCiAgICANCiAgICAjcHJvdmluY2UgZmFjdG9yDQogICAgZGF0YV96YSRwcm92aW5jZSA8LSBhcy5mYWN0b3IoZGF0YV96YSRwcm92aW5jZSkNCg0KICAgICMgMCBmb3IgTkFzDQogICAgZGF0YV96YSRjb3VudCA8LSBpZmVsc2UoaXMubmEoZGF0YV96YSRjb3VudCksIDAsIGRhdGFfemEkY291bnQpDQogICAgDQogICAgIyByZW1vdmUgbmVnYXRpdmVzDQogICAgZGF0YV96YSRjb3VudCA8LSBpZmVsc2UoZGF0YV96YSRjb3VudCA8IDAsIDAsIGRhdGFfemEkY291bnQpDQogICAgDQogICAgIyBnZXQgY3VtdWxhdGl2ZSBjb3VudHMNCiAgICBkYXRhX3phIDwtIGRhdGFfemEgJT4lDQogICAgICBncm91cF9ieShwcm92aW5jZSkgJT4lDQogICAgICBtdXRhdGUoY3VtdWxhdGl2ZV9jb3VudCA9IGN1bXN1bShjb3VudCkpICU+JQ0KICAgICAgdW5ncm91cCgpDQogICAgDQogICAgcmV0dXJuKGRhdGFfemEpDQogIH0NCmBgYA0KDQpCZWxvdyB3ZSB1c2UgdGhlIGZ1bmN0aW9uIGFib3ZlIHRvIHByb2Nlc3MgZGVhdGhzIGFuZCBjYXNlcyBhbmQgdGhlbiBjb21iaW5lIHRoZW0gaW50byBhIHNpbmdsZSBkYXRhc2V0Lg0KDQpgYGB7ciBhcHBseV9maXhfZGF0YV96YX0NCnN0YXJ0X2RhdGUgPC0gbWluKGFzLkRhdGUoZGF0YV96YV9jYXNlcyRkYXRlLCAiJWQtJW0tJVkiKSkNCmVuZF9kYXRlIDwtIG1heChhcy5EYXRlKGRhdGFfemFfY2FzZXMkZGF0ZSwgIiVkLSVtLSVZIikpDQpkYXRhX3phX2Nhc2VzIDwtDQogIGZpeF9kYXRhX3phKGRhdGFfemFfY2FzZXMsIHN0YXJ0X2RhdGUgPSBzdGFydF9kYXRlLCBlbmRfZGF0ZSA9IGVuZF9kYXRlKQ0KZGF0YV96YV9kZWF0aHMgPC0NCiAgZml4X2RhdGFfemEoZGF0YV96YV9kZWF0aHMsIHN0YXJ0X2RhdGUgPSBzdGFydF9kYXRlLCBlbmRfZGF0ZSA9IGVuZF9kYXRlKQ0KDQpkYXRhX3phX2Nhc2VzPC1jYmluZCgiY2FzZXMiLGRhdGFfemFfY2FzZXMpDQpkYXRhX3phX2RlYXRoczwtY2JpbmQoImRlYXRocyIsZGF0YV96YV9kZWF0aHMpDQpjb2xuYW1lcyhkYXRhX3phX2Nhc2VzKVsxXTwtInR5cGUiDQpjb2xuYW1lcyhkYXRhX3phX2RlYXRocylbMV08LSJ0eXBlIg0KDQojIGNvbWJpbmVkDQpkYXRhX3phIDwtDQogIHJiaW5kKGRhdGFfemFfY2FzZXMsZGF0YV96YV9kZWF0aHMpDQoNCiMgcmVtb3ZlIGRhdGEgc2V0cyBubyBsb25nZXIgbmVlZGVkDQpybSgiZGF0YV96YV9jYXNlcyIsICJkYXRhX3phX2RlYXRocyIsICJzdGFydF9kYXRlIiwgImVuZF9kYXRlIiwgImZpeF9kYXRhX3phIikNCmBgYA0KDQoNCiMjIE90aGVyIGNvdW50cmllcw0KDQojIyMgRG93bmxvYWQNCg0KRGF0YSBmb3Igb3RoZXIgY291bnRyaWVzIGFyZSBkb3dubG9hZGVkIGZyb20gW0BFQ0RDMjAyMF0uDQoNCmBgYHtyIGRvd25sb2FkX2RhdGEsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpHRVQoDQogIHVybCA9ICJodHRwczovL29wZW5kYXRhLmVjZGMuZXVyb3BhLmV1L2NvdmlkMTkvY2FzZWRpc3RyaWJ1dGlvbi9jc3YiLA0KICB3cml0ZV9kaXNrKA0KICAgICJlY2RjX2Nhc2VkaXN0cmlidXRpb24uY3N2IiwNCiAgICBvdmVyd3JpdGUgPSBUUlVFDQogICkNCikNCmBgYA0KDQojIyMgRGF0YSBwcmVwZXJhdGlvbg0KDQpGaXJzdCByZWFkIGluIHRoZSBkYXRhIGZyb20gdGhlIGRvd25sb2FkZWQgY29tbWEtc2VwYXJhdGVkIHZhbHVlcyB0ZXh0IGZpbGUuDQoNCmBgYHtyIHJlYWRfZGF0YX0NCiMgUmVhZCBmcm9tIENTVnMgYWJvdmUNCmRhdGEgPC0NCiAgcmVhZC5jc3YoDQogICAgImVjZGNfY2FzZWRpc3RyaWJ1dGlvbi5jc3YiLA0KICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICApDQpgYGANCg0KVXBkYXRlIGRhdGEgYnkgZG9pbmcgdGhlIGZvbGxvd2luZzoNCg0KMS4gRm9ybWF0IGRhdGVzLg0KMi4gU2V0IGNvbHVtbiBuYW1lcy4NCjMuIEFkZCBkYXRhIHdoZXJlIGRhdGVzIHdlcmUgc2tpcHBlZCAod2l0aCAwIGNhc2VzIG9yIGRlYXRocykNCjQuICJNZWx0IiB0aGUgZGF0YSBkb3duLg0KNS4gU2V0IGZpZWxkcyBhcyBmYWN0b3JzIGFzIG5lZWRlZC4NCjYuIEdldCBjdW11bGF0aXZlIG51bWJlcnMuDQoNCmBgYHtyIHVwZGF0ZV9kYXRhfQ0KIyBnZXQgZGF0ZQ0KZGF0YSRkYXRlIDwtIGFzLkRhdGUoZGF0YSRkYXRlUmVwLCBmb3JtYXQgPSAiJWQvJW0vJVkiKQ0KDQojIGdldCBjYXNlcyBhbmQgZGVhdGhzDQpkYXRhIDwtDQogIGRhdGEgJT4lIHNlbGVjdChjb250aW5lbnRFeHAsIGRhdGUsIGdlb0lkLCBjb3VudHJpZXNBbmRUZXJyaXRvcmllcywgY2FzZXMsIGRlYXRocykNCg0KIyBjb2x1bW4gbmFtZXMNCmNvbG5hbWVzKGRhdGEpIDwtDQogIGMoImNvbnRpbmVudCIsICJkYXRlIiwgImNvdW50cnlfY29kZSIsICJjb3VudHJ5IiwgImNhc2VzIiwgImRlYXRocyIpDQoNCiMgYWRkIDAgZGF0YSBmb3IgZGF0ZXMgdGhhdCB3ZXJlIHNraXBwZWQNCmZvciAoYyBpbiBsZXZlbHMoYXMuZmFjdG9yKGRhdGEkY291bnRyeSkpKSB7DQogIGNfZGF0ZXMgPC0gKGRhdGEgJT4lIGZpbHRlcihjb3VudHJ5ID09IGMpKSRkYXRlDQogIGNoZWNrX2RhdGVzIDwtIHNlcShtaW4oY19kYXRlcyksIG1heChjX2RhdGVzKSwgYnkgPSAxKQ0KICBtaXNzaW5nX2RhdGVzIDwtIGNoZWNrX2RhdGVzWyFjaGVja19kYXRlcyAlaW4lIGNfZGF0ZXNdDQogIGNvbnRpbmVudCA8LSAoZGF0YSAlPiUgZmlsdGVyKGNvdW50cnkgPT0gYykpJGNvbnRpbmVudFsxXQ0KICBjb3VudHJ5X2NvZGUgPC0gKGRhdGEgJT4lIGZpbHRlcihjb3VudHJ5ID09IGMpKSRjb3VudHJ5X2NvZGVbMV0NCiAgaWYgKGxlbmd0aChtaXNzaW5nX2RhdGVzKSA+IDApIHsNCiAgICBtaXNzaW5nX2RhdGEgPC0gZGF0YS5mcmFtZSgNCiAgICAgIGNvbnRpbmVudCA9IGNvbnRpbmVudCwNCiAgICAgIGRhdGUgPSBtaXNzaW5nX2RhdGVzLA0KICAgICAgY291bnRyeV9jb2RlID0gY291bnRyeV9jb2RlLA0KICAgICAgY291bnRyeSA9IHJlcChjLCBsZW5ndGgobWlzc2luZ19kYXRlcykpLA0KICAgICAgY2FzZXMgPSByZXAoMCwgbGVuZ3RoKG1pc3NpbmdfZGF0ZXMpKSwNCiAgICAgIGRlYXRocyA9IHJlcCgwLCBsZW5ndGgobWlzc2luZ19kYXRlcykpDQogICAgKQ0KICAgIGRhdGEgPC0gcmJpbmQoZGF0YSwgbWlzc2luZ19kYXRhKQ0KICB9DQp9DQoNCiMgIk1lbHQiIHRoZSBkYXRhDQpkYXRhIDwtDQogIHBpdm90X2xvbmdlcigNCiAgICBkYXRhLA0KICAgIGNvbHMgPSBjKCJjYXNlcyIsICJkZWF0aHMiKSwNCiAgICBuYW1lc190byA9ICJ0eXBlIiwNCiAgICB2YWx1ZXNfdG8gPSAiY291bnQiDQogICkNCg0KIyBmaWVsZHMgYXMgZmFjdG9ycw0KZGF0YSRjb250aW5lbnQgPC0gYXMuZmFjdG9yKGRhdGEkY29udGluZW50KQ0KZGF0YSRjb3VudHJ5X2NvZGUgPC0gYXMuZmFjdG9yKGRhdGEkY291bnRyeV9jb2RlKQ0KZGF0YSRjb3VudHJ5IDwtIGFzLmZhY3RvcihkYXRhJGNvdW50cnkpDQpkYXRhJHR5cGUgPC0gYXMuZmFjdG9yKGRhdGEkdHlwZSkNCg0KIyBtYWtlIHN1cmUgaXQgaXMgYSBkYXRhIGZyYW1lDQpkYXRhIDwtIGFzLmRhdGEuZnJhbWUoZGF0YSkNCg0KIyB6ZXJvaXNlIGNvdW50cyBiZWxvdyAwDQpkYXRhJGNvdW50IDwtICBpZmVsc2UoZGF0YSRjb3VudDwwLCAwICwgZGF0YSRjb3VudCkNCg0KI29yZGVyIGFuZCBnZXQgZ3JvdXAgYnkNCmRhdGEgPC0gIGRhdGEgJT4lDQogIGdyb3VwX2J5KGNvdW50cnlfY29kZSwgY291bnRyeSwgdHlwZSkgJT4lDQogIGFycmFuZ2UoY291bnRyeV9jb2RlLCBjb3VudHJ5LCB0eXBlLCBkYXRlKSAlPiUNCiAgbXV0YXRlKGN1bXVsYXRpdmVfY291bnQgPSBjdW1zdW0oY291bnQpKSAlPiUNCiAgdW5ncm91cCgpDQoNCmBgYA0KDQojIyBHb29nbGUgTW9iaWxpdHkgRGF0YQ0KDQpHb29nbGUgcmVsZWFzZWQgbW9iaWxpdHkgZGF0YSBpbmRleGVzIHRoYXQgZGV2aWF0aW9ucyBmcm9tIGEgYmFzZWxpbmUgb2YgbW92ZW1lbnQgZHVyaW5nIDMgSmFudWFyeSB0byA2IEZlYnJ1YXJ5IDIwMjAgW0BHb29nbGUyMDIwXS4gIFRoaXMgZGF0YSBpcyBkb3dubG9hZGVkIGFuZCBwcmVwYXJlZCBmb3IgbGlua2luZyB0byBvdXIgZXN0aW1hdGVzIG9mICRSX3t0LG19JC4NCg0KIyMjIERvd25sb2FkDQoNClRoZSBkYXRhIGlzIG1hZGUgYXZhaWxhYmxlIGFzIGEgY29tbWEtc2VwYXJhdGVkIGZpbGUgd2hpY2ggaXMgdXBkYXRlZCByZWd1bGFybHkgYnV0IG5vdCBkYWlseS4gIERhdGEgaXMgc29tZXRpbWVzIHVwIHRvIGEgd2VlayBiZWhpbmQuDQoNCmBgYHtyIGRvd25sb2FkX2dvb2dsZV9tb2JpbGl0eSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCkdFVCgNCiAgdXJsID0gImh0dHBzOi8vd3d3LmdzdGF0aWMuY29tL2NvdmlkMTkvbW9iaWxpdHkvR2xvYmFsX01vYmlsaXR5X1JlcG9ydC5jc3YiLA0KICB3cml0ZV9kaXNrKA0KICAgICJnb29nbGVfbW9iaWxpdHlfZGF0YS5jc3YiLA0KICAgIG92ZXJ3cml0ZSA9IFRSVUUNCiAgKQ0KKQ0KYGBgDQoNCiMjIyBQcmVwYXJpbmcgdGhlIG1vYmlsaXR5IGRhdGENCg0KRmlyc3QgcmVhZCBpbiB0aGUgZmlsZToNCg0KYGBge3IgcmVhZF9nb29nbGVfbW9iaWxpdHl9DQojIFJlYWQgZnJvbSBDU1ZzIGFib3ZlDQpnb29nbGVfbW9iaWxpdHlfZGF0YSA8LQ0KICByZWFkLmNzdigNCiAgICAiZ29vZ2xlX21vYmlsaXR5X2RhdGEuY3N2IiwNCiAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsDQogICAgbmEuc3RyaW5ncyA9ICIiDQogICkNCmBgYA0KDQpCZWxvdyB0aGUgZGF0YSBpcyBwcmVwYXJlZCBieSBkb2luZyB0aGUgZm9sbG93aW5nOg0KDQoxLiBGaXhpbmcgZGF0ZXMNCjIuIFJldGFpbmluZyBvbmx5IGNvdW50cnkgbGV2ZWwgbW9iaWxpdHkgZGF0YSwgZXhjZXB0IGZvciBTb3V0aCBBZnJpY2Egd2hlcmUgY291bnRyeSBhbmQgcHJvdmluY2lhbCBkYXRhIGlzIHJldGFpbmVkLg0KMy4gUmVuYW1pbmcgdGhlIGZpZWxkcyBjb25zaXN0ZW50bHkuDQo0LiBWYXJpb3VzIHRyYW5zZm9ybWF0aW9ucyB0byBtYWtlIGpvaW5pbmcgdG8gb3RoZXIgZGF0YSBlYXNpZXIgbGF0ZXIuDQoNCmBgYHtyIHByZXBfZ29vZ2xlX21vYmlsaXR5fQ0KIyBmaXggZGF0ZXMNCmdvb2dsZV9tb2JpbGl0eV9kYXRhJGRhdGUgPC0NCiAgYXMuRGF0ZShnb29nbGVfbW9iaWxpdHlfZGF0YSRkYXRlLCBmb3JtYXQgPSAiJVktJW0tJWQiKQ0KDQojIGtlZXAgb25seSBjb3VudHJ5IGxldmVsIGRhdGEgYnV0IGtlZXAgYWxsIGRhdGEgZm9yIFpBDQpnb29nbGVfbW9iaWxpdHlfZGF0YSA8LSBnb29nbGVfbW9iaWxpdHlfZGF0YSAlPiUNCiAgZmlsdGVyKGNvdW50cnlfcmVnaW9uX2NvZGUgPT0gIlpBIiB8IGlzLm5hKHN1Yl9yZWdpb25fMSkpDQoNCiMgcGljayBvbmx5IHRoZSBmaWVsZHMgcmVxdWlyZWQNCmdvb2dsZV9tb2JpbGl0eV9kYXRhIDwtIGdvb2dsZV9tb2JpbGl0eV9kYXRhICU+JQ0KICBzZWxlY3QoDQogICAgY291bnRyeV9yZWdpb25fY29kZSwNCiAgICBjb3VudHJ5X3JlZ2lvbiwNCiAgICBzdWJfcmVnaW9uXzEsDQogICAgaXNvXzMxNjZfMl9jb2RlLA0KICAgIGRhdGUsDQogICAgcmV0YWlsX2FuZF9yZWNyZWF0aW9uX3BlcmNlbnRfY2hhbmdlX2Zyb21fYmFzZWxpbmUsDQogICAgZ3JvY2VyeV9hbmRfcGhhcm1hY3lfcGVyY2VudF9jaGFuZ2VfZnJvbV9iYXNlbGluZSwNCiAgICBwYXJrc19wZXJjZW50X2NoYW5nZV9mcm9tX2Jhc2VsaW5lLA0KICAgIHRyYW5zaXRfc3RhdGlvbnNfcGVyY2VudF9jaGFuZ2VfZnJvbV9iYXNlbGluZSwNCiAgICB3b3JrcGxhY2VzX3BlcmNlbnRfY2hhbmdlX2Zyb21fYmFzZWxpbmUsDQogICAgcmVzaWRlbnRpYWxfcGVyY2VudF9jaGFuZ2VfZnJvbV9iYXNlbGluZQ0KICApDQoNCiMgcmVuYW1lIHRoZSBmaWVsZHMNCmNvbG5hbWVzKGdvb2dsZV9tb2JpbGl0eV9kYXRhKSA8LSBjKA0KICAiY291bnRyeV9jb2RlIiwNCiAgImNvdW50cnkiLA0KICAicHJvdmluY2UiLA0KICAicHJvdmluY2VfY29kZSIsDQogICJkYXRlIiwNCiAgInJldGFpbF9hbmRfcmVjcmVhdGlvbiIsDQogICJncm9jZXJ5X2FuZF9waGFybWFjeSIsDQogICJwYXJrcyIsDQogICJ0cmFuc2l0X3N0YXRpb25zIiwNCiAgIndvcmtwbGFjZXMiLA0KICAicmVzaWRlbnRpYWwiDQopDQoNCiMgbW92ZSBwcm92aW5jZSBkYXRhIHRvIGNvdW50cnkgZm9yIGVhc2UgbGF0ZXINCmdvb2dsZV9tb2JpbGl0eV9kYXRhJGNvdW50cnlfY29kZVshaXMubmEoZ29vZ2xlX21vYmlsaXR5X2RhdGEkcHJvdmluY2VfY29kZSldIDwtDQogIGdvb2dsZV9tb2JpbGl0eV9kYXRhJHByb3ZpbmNlX2NvZGVbIWlzLm5hKGdvb2dsZV9tb2JpbGl0eV9kYXRhJHByb3ZpbmNlX2NvZGUpXQ0KZ29vZ2xlX21vYmlsaXR5X2RhdGEkY291bnRyeVshaXMubmEoZ29vZ2xlX21vYmlsaXR5X2RhdGEkcHJvdmluY2VfY29kZSldIDwtDQogIGdvb2dsZV9tb2JpbGl0eV9kYXRhJHByb3ZpbmNlWyFpcy5uYShnb29nbGVfbW9iaWxpdHlfZGF0YSRwcm92aW5jZV9jb2RlKV0NCg0KIyByZW5hbWUgS1pOIGNvbnNpc3RlbnRseQ0KZ29vZ2xlX21vYmlsaXR5X2RhdGEkY291bnRyeV9jb2RlW2dvb2dsZV9tb2JpbGl0eV9kYXRhJGNvdW50cnlfY29kZSA9PSAiWkEtTkwiXSA8LQ0KICAiWkEtS1pOIg0KDQojIGRyb3AgcHJvdmluY2UgY29sdW1ucw0KZ29vZ2xlX21vYmlsaXR5X2RhdGEgPC0gZ29vZ2xlX21vYmlsaXR5X2RhdGFbLCBjKC0zLC00KV0NCg0KIyBkaXZpZGUgaW5kZXhlcyBieSAxMDAgdG8gaGF2ZSBudW1iZXJzIGJldHdlZW4gMCBhbmQgMQ0KZ29vZ2xlX21vYmlsaXR5X2RhdGFbLCA0OjldIDwtIGdvb2dsZV9tb2JpbGl0eV9kYXRhWywgNDo5XSAvIDEwMA0KDQojIGZpeCBHQiBjb2RlIHRvIFVLDQpnb29nbGVfbW9iaWxpdHlfZGF0YSRjb3VudHJ5X2NvZGVbZ29vZ2xlX21vYmlsaXR5X2RhdGEkY291bnRyeV9jb2RlID09ICJHQiJdIDwtDQogICJVSyINCg0KIyBtYWtlIGZhY3RvcnMNCmdvb2dsZV9tb2JpbGl0eV9kYXRhJGNvdW50cnlfY29kZSA8LQ0KICBhcy5mYWN0b3IoZ29vZ2xlX21vYmlsaXR5X2RhdGEkY291bnRyeV9jb2RlKQ0KZ29vZ2xlX21vYmlsaXR5X2RhdGEkY291bnRyeSA8LQ0KICBhcy5mYWN0b3IoZ29vZ2xlX21vYmlsaXR5X2RhdGEkY291bnRyeSkNCmBgYA0KDQpGdXJ0aGVyIHRvIHRoZSBhYm92ZSB3ZSBhbHNvIGNhbGN1bGF0ZSBhbiBhdmVyYWdlIG1vYmlsaXR5IGluZGV4IHBlciBbQE5vdXZlbGxldDIwMjBdLiAgVGhpcyBpbmRleCBhdmVyYWdlcyB0aGUgbW9iaWxpdHkgaW5kZXhlcyBidXQgZXhjbHVkZSB0aGUgZm9sbG93aW5nIGluZGV4ZXM6DQoNCiogUmVzaWRlbnRpYWwgaW5kZXggaXMgZXhjbHVkZWQgYXMgaXQncyBsZXNzIGxpa2VseSB0byBkcml2ZSB0aGUgZXBpZGVtaWMgKGFuZCBpcyBpbnZlcnNlbHkgY29ycmVsYXRlZCksDQoqIE1vYmlsaXR5IGluIHBhcmtzIGlzIGFsc28gdW5saWtlbHkgdG8gYmUgYSBkcml2aW5nIGZhY3Rvci4NCg0KYGBge3IgY2FsY19hdmVyYWdlX21vYmlsaXR5fQ0KZ29vZ2xlX21vYmlsaXR5X2RhdGEkYXZlcmFnZV9tb2JpbGl0eSA8LSAoDQogIGdvb2dsZV9tb2JpbGl0eV9kYXRhJHJldGFpbF9hbmRfcmVjcmVhdGlvbiArDQogICAgZ29vZ2xlX21vYmlsaXR5X2RhdGEkZ3JvY2VyeV9hbmRfcGhhcm1hY3kgKw0KICAgIGdvb2dsZV9tb2JpbGl0eV9kYXRhJHRyYW5zaXRfc3RhdGlvbnMgKw0KICAgIGdvb2dsZV9tb2JpbGl0eV9kYXRhJHdvcmtwbGFjZXMNCikgLyA0DQpgYGANCg0KIyMgQ29tYmluZSBTb3V0aCBBZnJpY2FuIGRhdGEgd2l0aCBvdGhlciBkYXRhDQoNClRvIHNpbXBsaWZ5IGZ1cnRoZXIgYW5hbHlzaXMgaXQncyBlYXNpZXIgdG8gY29tYmluZSBhbGwgZGF0YSBpbiBhIHNpbmdsZSBkYXRhc2V0Og0KDQpgYGB7ciBjb21iaW5lX2RhdGF9DQojIEFkZCBhICJjb250aW5lbnQiIGZpZWxkIHRvIGRhdGFfemEgYW5kIGNvbWJpbmUNCmRhdGFfemEgPC0gY2JpbmQoIlNBIiwgZGF0YV96YSkNCmNvbG5hbWVzKGRhdGFfemEpWzFdIDwtICJjb250aW5lbnQiDQpjb2xuYW1lcyhkYXRhX3phKVs0XSA8LSAiY291bnRyeSINCmRhdGFfemEkY291bnRyeV9jb2RlIDwtIHBhc3RlMCgiWkEtIiwgZGF0YV96YSRjb3VudHJ5KQ0KZGF0YSA8LSByYmluZChkYXRhLCBkYXRhX3phKQ0KDQojIHJlbW92ZSBhbGwgb2JqZWN0cyBleGNlcHQgd2hhdCB3ZSBuZWVkDQpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIGMoImRhdGEiLCAiY291bnRyeV9jb2RlcyIsICJnb29nbGVfbW9iaWxpdHlfZGF0YSIpXSkNCmBgYA0KDQojIEJhc2ljIEV4cGxvcmF0aW9uDQoNCiMjIFNvdXRoIEFmcmljYQ0KDQpCZWxvdyB3ZSBwbG90IGN1bXVsYXRpdmUgY2FzZSBjb3VudCBvbiBhIGxvZyBzY2FsZSBieSBwcm92aW5jZToNCg0KYGBge3IgcGxvdF9jYXNlc196YX0NCmdncGxvdCgNCiAgZGF0YSAlPiUgZmlsdGVyKGNvbnRpbmVudCA9PSAiU0EiICYNCiAgICAgICAgICAgICAgICAgICAgdHlwZSA9PSAiY2FzZXMiICYgY3VtdWxhdGl2ZV9jb3VudCA+IDApLA0KICBhZXMoeCA9IGRhdGUsIHkgPSBjdW11bGF0aXZlX2NvdW50KQ0KKSArDQogIGdlb21fbGluZShhZXMoY29sb3IgPSBjb3VudHJ5KSwgc2l6ZSA9IDEpICsNCiAgc2NhbGVfeV9sb2cxMChsYWJlbHMgPSBjb21tYSkgKw0KICBnZ3RpdGxlKCJDdW11bGF0aXZlIENhc2VzIGJ5IFByb3ZpbmNlIikgKw0KICB4bGFiKCJEYXRlIikgKw0KICB5bGFiKCJDdW11bGF0aXZlIENhc2VzIikgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKw0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChuY29sID0gMSkpICsNCiAgc2NhbGVfY29sb3JfaHVlKGwgPSA1MCkNCmBgYA0KDQpCZWxvdyB3ZSBwbG90IHRoZSBjdW11bGF0aXZlIGRlYXRocyBieSBwcm92aW5jZSBvbiBhIGxvZyBzY2FsZToNCg0KYGBge3IgcGxvdF9kZWF0aHNfemF9DQpnZ3Bsb3QoDQogIGRhdGEgJT4lIGZpbHRlcihjb250aW5lbnQgPT0gIlNBIiAmDQogICAgICAgICAgICAgICAgICAgIHR5cGUgPT0gImRlYXRocyIgJiBjdW11bGF0aXZlX2NvdW50ID4gMCksDQogIGFlcyh4ID0gZGF0ZSwgeSA9IGN1bXVsYXRpdmVfY291bnQpDQopICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IGNvdW50cnkpLCBzaXplID0gMSkgKw0KICBzY2FsZV95X2xvZzEwKGxhYmVscyA9IGNvbW1hKSArDQogIGdndGl0bGUoIkN1bXVsYXRpdmUgRGVhdGhzIGJ5IFByb3ZpbmNlIikgKw0KICB4bGFiKCJEYXRlIikgKw0KICB5bGFiKCJDdW11bGF0aXZlIERlYXRocyIpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobmNvbCA9IDEpKSArDQogIHNjYWxlX2NvbG9yX2h1ZShsID0gNTApDQpgYGANCg0KQmVsb3cgd2UgcGxvdCBhdmVyYWdlIG1vYmlsaXR5IGluZGV4ZXMgYnkgcHJvdmluY2U6DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBnb29nbGVfbW9iaWxpdHlfZGF0YVtzdWJzdHIoYXMuY2hhcmFjdGVyKGdvb2dsZV9tb2JpbGl0eV9kYXRhJGNvdW50cnlfY29kZSksIDEsIDIpID09DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlpBIiwgXSwNCiAgICAgICBhZXMoeCA9IGRhdGUsIHkgPSBhdmVyYWdlX21vYmlsaXR5LCBjb2xvdXIgPSBjb3VudHJ5KSkgKw0KICBnZW9tX2xpbmUoKSArIHNjYWxlX2NvbG9yX2h1ZShsID0gNTApICsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCiMjIE90aGVyIENvdW50cmllcw0KDQpCZWxvdyB3ZSBwbG90IGN1bXVsYXRpdmUgY2FzZSBjb3VudCBvbiBhIGxvZyBzY2FsZSBieSBjb3VudHJ5Og0KDQpgYGB7ciBwbG90X2Nhc2VzfQ0KZ2dwbG90KA0KICBkYXRhICU+JSBmaWx0ZXIoDQogICAgY29udGluZW50ICE9ICJTQSIgJg0KICAgICAgdHlwZSA9PSAiY2FzZXMiICYNCiAgICAgIGNvdW50cnlfY29kZSAlaW4lIGNvdW50cnlfY29kZXMgJg0KICAgICAgY3VtdWxhdGl2ZV9jb3VudCA+IDANCiAgKSwNCiAgYWVzKHggPSBkYXRlLCB5ID0gY3VtdWxhdGl2ZV9jb3VudCkNCikgKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yID0gY291bnRyeSksIHNpemUgPSAxKSArDQogIHNjYWxlX3lfbG9nMTAobGFiZWxzID0gY29tbWEpICsNCiAgZ2d0aXRsZSgiQ3VtdWxhdGl2ZSBDYXNlcyBieSBDb3VudHJ5IikgKw0KICB4bGFiKCJEYXRlIikgKw0KICB5bGFiKCJDdW11bGF0aXZlIENhc2VzIikgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKw0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChuY29sID0gMSkpICsNCiAgc2NhbGVfY29sb3JfaHVlKGwgPSA1MCkNCmBgYA0KDQpCZWxvdyB3ZSBwbG90IHRoZSBjdW11bGF0aXZlIGRlYXRocyBieSBjb3VudHJ5IG9uIGEgbG9nIHNjYWxlOg0KDQpgYGB7ciBwbG90X2RlYXRoc30NCmdncGxvdCgNCiAgZGF0YSAlPiUgZmlsdGVyKA0KICAgIGNvbnRpbmVudCAhPSAiU0EiICYNCiAgICAgIHR5cGUgPT0gImRlYXRocyIgJg0KICAgICAgY291bnRyeV9jb2RlICVpbiUgY291bnRyeV9jb2RlcyAmIGN1bXVsYXRpdmVfY291bnQgPiAwDQogICksDQogIGFlcyh4ID0gZGF0ZSwgeSA9IGN1bXVsYXRpdmVfY291bnQpDQopICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IGNvdW50cnkpLCBzaXplID0gMSkgKw0KICBzY2FsZV95X2xvZzEwKGxhYmVscyA9IGNvbW1hKSArDQogIGdndGl0bGUoIkN1bXVsYXRpdmUgRGVhdGhzIGJ5IENvdW50cnkiKSArDQogIHhsYWIoIkRhdGUiKSArDQogIHlsYWIoIkN1bXVsYXRpdmUgRGVhdGhzIikgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKw0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChuY29sID0gMSkpICsNCiAgc2NhbGVfY29sb3JfaHVlKGwgPSA1MCkNCmBgYA0KDQpCZWxvdyB3ZSBwbG90IGF2ZXJhZ2UgbW9iaWxpdHkgaW5kZXhlcyBieSBjb3VudHJ5Og0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gZ29vZ2xlX21vYmlsaXR5X2RhdGEgJT4lIGZpbHRlcihjb3VudHJ5X2NvZGUgJWluJSBjb3VudHJ5X2NvZGVzKSwNCiAgICAgICBhZXMoeCA9IGRhdGUsIHkgPSBhdmVyYWdlX21vYmlsaXR5LCBjb2xvdXIgPSBjb3VudHJ5KSkgKw0KICBnZW9tX2xpbmUoKSArIHNjYWxlX2NvbG9yX2h1ZShsID0gNTApICsNCiAgdGhlbWVfYncoKQ0KYGBgDQojIFNlcmlhbCBJbnRlcnZhbA0KDQpUbyBkbyBmdXJ0aGVyIGFuYWx5c2lzIGFuIHNlcmlhbCBpbnRlcnZhbCBhc3N1bXB0aW9uIGlzIG5lZWRlZC4gIFRoZSBzZXJpYWwgaW50ZXJ2YWwgaXMgdGFrZW4gZnJvbSBbQEZlcmd1c29uMjAyMF0uICBJdCdzIGFzc3VtZWQgdG8gYmUgR2FtbWEgZGlzdHJpYnV0ZWQgd2l0aCBtZWFuIG9mIDYuNDggYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiAzLjgzLiAgVGhpcyBjb3JyZXNwb25kcyB0byB0aGUgZWZmZWN0aXZlIGluZmVjdGlvdXNuZXNzIG9mIGFuIGluZGl2aWR1YWwgc2luY2UgYWNxdWlyaW5nIHRoZSBpbmZlY3Rpb24gdGhlbXNlbHZlcy4gIA0KDQpXZSBwbG90IHRoaXMgc2VyaWFsIGRpc3RyaWJ1dGlvbiBiZWxvdzoNCg0KYGBge3Igc2VyaWFsX2ludGVydmFsfQ0Kc2lfbWVhbiA8LSA2LjQ4DQpzaV9zZCA8LSAzLjgzDQpzaV9jdiA8LSBzaV9zZCAvIHNpX21lYW4NCg0Kc2VyaWFsX2ludGVydmFsID0gcmVwKDAsIDEpDQpzZXJpYWxfaW50ZXJ2YWxbMV0gPSAocGdhbW1hQWx0KDEuNSwgc2lfbWVhbiwgc2lfY3YpIC0gcGdhbW1hQWx0KDAsIHNpX21lYW4sIHNpX2N2KSkNCmZvciAoaSBpbiAyOjUwKSB7DQogIHNlcmlhbF9pbnRlcnZhbFtpXSA9IChwZ2FtbWFBbHQoaSArIC41LCBzaV9tZWFuLCBzaV9jdikgLSBwZ2FtbWFBbHQoaSAtIC41LCBzaV9tZWFuLCBzaV9jdikpDQp9DQpnZ3Bsb3QoDQogIGRhdGEuZnJhbWUoZGF5cyA9IHNlcSgxLCAyMCwgMSksIHNlcmlhbF9pbnRlcnZhbCA9IHNlcmlhbF9pbnRlcnZhbFsxOjIwXSksDQogIGFlcyh5ID0gc2VyaWFsX2ludGVydmFsLCB4ID0gZGF5cykNCikgKyBnZW9tX2xpbmUoc2l6ZSA9IDEpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobmNvbCA9IDEpKSArDQogIHNjYWxlX2NvbG9yX2h1ZShsID0gNTApDQoNCmBgYA0KDQojIEVzdGltYXRlICRSX3t0LG19JCB2YWx1ZXMgYnkgd2Vlaw0KDQojIyBFc3RpbWF0aW9uIFJvdXRpbmUNCg0KVGhlIGZvbGxvd2luZyBjb2RlIHdvcmtzIGJhY2t3YXJkcyBhbmQgZml0cyAkUl97dCxtfSQgdmFsdWVzIGZvciB3aG9sZSB3ZWVrcyAoZW5kaW5nIG9uIHRoZSBsYXN0IGRhdGUgaW4gdGhlIGRhdGEpIHVzaW5nIHRoZSBgRXBpRXN0aW1gIHBhY2thZ2UuIFVzaW5nIGRlYXRocyBvciBpbmZlY3Rpb24gdGhlIGFzIHRoZSBjYXNlcyBpbiB0aGUgcHV0cyB0aGUgZXN0aW1hdGUgb2YgJFJfe3QsbX0kIGFzIGF0IHRoZSBkYXRlIG9mIHRoZSBjYXNlcyBiZWluZyB0cmFja2VkLg0KDQpTbyAkdCQgaXMgYmFzZWQgb24gdGhlIGRhdGUgb2YgcmVwb3J0aW5nIChiZSB0aGF0IGNhc2VzIG9yIGRlYXRocykgYW5kICRtJCBpcyB0aGUgY291bnRyeSBvciBwcm92aW5jZS4gVHdvIHZhbHVlcyBhcmUgZXN0aW1hdGVkIGZvciBlYWNoIGNvdW50cnk6DQoNCjEuICRSX3t0LG19XntjYXNlc30kIGlzIHRoZSByZXByb2R1Y3RpdmUgbnVtYmVyIGltcGxpZWQgYnkgdGhlIGNhc2VzIHJlcG9ydGVkIGF0IHRpbWUgJHQkIGluIGNvdW50cnkgJG0kLg0KMi4gJFJfe3QsbX1ee2RlYXRoc30kIGlzIHRoZSByZXByb2R1Y3RpdmUgbnVtYmVyIGltcGxpZWQgYnkgdGhlIGRlYXRocyByZXBvcnRlZCBhdCB0aW1lICR0JCBpbiBjb3VudHJ5ICRtJC4NCg0KTm90ZSB0aGF0IHRoZSB0aW1lIHBlcmlvZHMgYXJlIGxlZnQgdW5hZGp1c3RlZCwgdGhvdWdoIGluIHJlYWxpdHkgdGhlICRSX3t0LG19XntkZWF0aHN9JCBzaG91bGQgYmUgc2hpZnRlZCBiYWNrIGFwcHJveGltYXRlbHkgMiB3ZWVrcyByZWxhdGl2ZSB0byAkUl97dCxtfV57Y2FzZXN9JC4NCg0KYGBge3IgZXN0aW1hdGVfcnR9DQpSdF9kYXRhIDwtIE5VTEwNCmZvciAoYyBpbiBsZXZlbHMoZGF0YSRjb3VudHJ5KSkgew0KICBmb3IgKHQgaW4gbGV2ZWxzKGRhdGEkdHlwZSkpIHsNCiAgICAjIGZpbHRlciBvdXQgY291bnRyeSBkYXRhIG9mIHR5cGUgdA0KICAgIGNfZGF0YSA8LSBkYXRhICU+JSBmaWx0ZXIoY291bnRyeSA9PSBjICYgdHlwZSA9PSB0KQ0KICAgIA0KICAgICMgdmVjdG9yIG9mIGNvdW50IG9mIGNhc2VzL2RlYXRocw0KICAgIEkgPC0gY19kYXRhJGNvdW50DQogICAgDQogICAgIyB0PTEgY29ycmVzcG9uZHMgdG8gdGhpcyBkYXRlOg0KICAgIHQxX2RhdGUgPC0gbWluKGNfZGF0YSRkYXRlKQ0KICAgIA0KICAgICMgdGhlIGRheSBhZnRlciB0aGUgZmlyc3QgY2FzZS9kZWF0aDoNCiAgICB0X3N0YXJ0X2RhdGUgPC0gbWluKChjX2RhdGEgJT4lIGZpbHRlcihjb3VudCA+IDApKSRkYXRlKSArIDENCiAgICB0X3N0YXJ0IDwtIG1pbihzZXEoMSwgbGVuZ3RoKEkpKVtJID4gMF0pICsgMQ0KICAgIA0KICAgICMgbGFzdCBkYXkgb2YgY2FzZXMvZGVhdGhzDQogICAgdF9lbmQgPC0gbGVuZ3RoKEkpDQogICAgdF9lbmRfZGF0ZSA8LSAgdDFfZGF0ZSArICh0X2VuZCAtIDEpDQogICAgDQogICAgIyBob3cgbWFueSBmdWxsIHdlZWtzIGRvIHdlIGhhdmUNCiAgICBmdWxsX3dlZWtzIDwtIGZsb29yKCh0X2VuZCAtIHRfc3RhcnQpIC8gNykNCiAgICANCiAgICAjIG9ubHkgY29udGludWUgaWYgd2UgaGF2ZSAxIGZ1bGwgd2VlayBvciBtb3JlDQogICAgaWYgKGZ1bGxfd2Vla3MgPiAwKSB7DQogICAgICAjIHRoZW4gZGl2aWRlIHBlcmlvZCBpbnRvIHdlZWtzDQogICAgICBULlN0YXJ0IDwtIHNlcSh0X2VuZCAtIDcgKiBmdWxsX3dlZWtzLCB0X2VuZCAtIDcsIGJ5ID0gNykNCiAgICAgIFQuRW5kIDwtIFQuU3RhcnQgKyA3DQogICAgICANCiAgICAgICMgZXN0aW1hdGUgJFJfe3QsbX1ee3R5cGV9JCBmb3IgZWFjaCB3ZWVrOg0KICAgICAgY19SdCA8LSBFc3RpbWF0ZVIoDQogICAgICAgIEksDQogICAgICAgIFQuU3RhcnQgPSBULlN0YXJ0LA0KICAgICAgICBULkVuZCA9IFQuRW5kLA0KICAgICAgICBNZWFuLlNJID0gc2lfbWVhbiwNCiAgICAgICAgU3RkLlNJID0gc2lfc2QsDQogICAgICAgIG1ldGhvZCA9ICJQYXJhbWV0cmljU0kiDQogICAgICApJFINCiAgICAgIA0KICAgICAgIyBjb3VudCBjYXNlcw0KICAgICAgY19kYXRhJHdlZWsgPC0NCiAgICAgICAgZmxvb3IoYXMubnVtZXJpYyhjX2RhdGEkZGF0ZSAtICh0MV9kYXRlICsgVC5TdGFydFsxXSkpIC8gNykNCiAgICAgIGNfZGF0YSRkZCA8LQ0KICAgICAgICBjKDAsIGNfZGF0YSRkYXRlWzI6bnJvdyhjX2RhdGEpXSAtIGNfZGF0YSRkYXRlWzE6KG5yb3coY19kYXRhKSAtIDEpXSkNCiAgICAgIGNfYWdnX2RhdGEgPC0gY19kYXRhICU+JQ0KICAgICAgICBmaWx0ZXIod2VlayA+PSAwKSAlPiUNCiAgICAgICAgZ3JvdXBfYnkod2VlaykgJT4lDQogICAgICAgIHN1bW1hcmlzZShjb3VudCA9IHN1bShjb3VudCkpICU+JQ0KICAgICAgICB1bmdyb3VwKCkNCiAgICAgIA0KICAgICAgIyBhZGQgY291bnRyeSBhbmQgdHlwZSBkZXNpZ25hdGlvbnMNCiAgICAgIGNfUnQgPC0NCiAgICAgICAgY2JpbmQoY19kYXRhJGNvbnRpbmVudFsxXSwNCiAgICAgICAgICAgICAgY19kYXRhJGNvdW50cnlfY29kZVsxXSwNCiAgICAgICAgICAgICAgYywNCiAgICAgICAgICAgICAgdCwNCiAgICAgICAgICAgICAgY19SdCwNCiAgICAgICAgICAgICAgY19hZ2dfZGF0YSRjb3VudCkNCiAgICAgIA0KICAgICAgIyBhbmQgYWRkIGRhdGVzDQogICAgICBjX1J0JGRhdGVfc3RhcnQgPC0gdDFfZGF0ZSArIGNfUnQkVC5TdGFydA0KICAgICAgY19SdCRkYXRlX2VuZCA8LSB0MV9kYXRlICsgY19SdCRULkVuZCAtIDENCiAgICAgIA0KICAgICAgIyBjb21iaW5lIHRoZSByZXN1bHRzDQogICAgICBpZiAoaXMubnVsbChSdF9kYXRhKSkgew0KICAgICAgICBSdF9kYXRhIDwtIGNfUnQNCiAgICAgIH0gZWxzZSB7DQogICAgICAgIFJ0X2RhdGEgPC0gcmJpbmQoUnRfZGF0YSwgY19SdCkNCiAgICAgIH0NCiAgICB9DQogIH0NCn0NCmBgYA0KDQojIyBQcmVwcGluZyBkYXRhIGZvciB0YWJ1bGF0aW9uIGFuZCBwbG90cw0KDQpCZWxvdyB3ZSBwcmVwIGNvbHVtbiBoZWFkaW5ncyBhbmQgcHJlcGFyZSBDSSBkYXRhOg0KDQpgYGB7ciBwcmVwX3J0X2RhdGF9DQojIENvbHVtbiBuYW1lcw0KY29sbmFtZXMoUnRfZGF0YSkgPC0gYygNCiAgImNvbnRpbmVudCIsDQogICJjb3VudHJ5X2NvZGUiLA0KICAiY291bnRyeSIsDQogICJ0eXBlIiwNCiAgInRfc3RhcnQiLA0KICAidF9lbmQiLA0KICAiUnRfbWVhbiIsDQogICJSdF9zdGQiLA0KICAiUnRfbGlfOTUiLA0KICAiUnRfbGlfOTAiLA0KICAiUnRfbGlfNTAiLA0KICAiUnRfbWVkaWFuIiwNCiAgIlJ0X3VpXzUwIiwNCiAgIlJ0X3VpXzkwIiwNCiAgIlJ0X3VpXzk1IiwNCiAgImNvdW50IiwNCiAgImRhdGVfc3RhcnQiLA0KICAiZGF0ZV9lbmQiDQopDQoNCiMgbWlkIHdlZWsgZGF0YSBwb2ludA0KUnRfZGF0YSRkYXRlX21pZCA8LSBSdF9kYXRhJGRhdGVfc3RhcnQrKFJ0X2RhdGEkZGF0ZV9lbmQtUnRfZGF0YSRkYXRlX3N0YXJ0KS8yDQoNCiMgc3BsaXQgb3V0IENJIGRhdGEgaW50byBkaWZmZXJlbnQgZGF0YXNldA0KUnRfY2lfZGF0YTwtIE5VTEwNCmZvciAoY2kgaW4gYygiNTAiLCI5MCIsICI5NSIpKSB7DQogIHIgPC0gZGF0YS5mcmFtZSgNCiAgICBjb3VudHJ5X2NvZGU9UnRfZGF0YSRjb3VudHJ5X2NvZGUsDQogICAgY291bnRyeT1SdF9kYXRhJGNvdW50cnksDQogICAgdHlwZT1SdF9kYXRhJHR5cGUsDQogICAgY2k9cmVwKHBhc3RlMChjaSwiJSIpLG5yb3coUnRfZGF0YSkpLA0KICAgIGRhdGVfbWlkPVJ0X2RhdGEkZGF0ZV9taWQsDQogICAgUnRfbWVhbj1SdF9kYXRhJFJ0X21lYW4sDQogICAgUnRfbGk9UnRfZGF0YVtbcGFzdGUwKCJSdF9saV8iLGNpKV1dLA0KICAgIFJ0X3VpPVJ0X2RhdGFbW3Bhc3RlMCgiUnRfdWlfIixjaSldXQ0KICApDQogIFJ0X2NpX2RhdGE8LXJiaW5kKHIsUnRfY2lfZGF0YSkNCn0NCg0KIyByZW1vdmUgd2hhdCBpcyBub3QgbmVlZGVkDQpybShsaXN0PWxzKClbIWxzKCkgJWluJSBjKCJkYXRhIiwiY291bnRyeV9jb2RlcyIsIlJ0X2RhdGEiLCJSdF9jaV9kYXRhIiwgImdvb2dsZV9tb2JpbGl0eV9kYXRhIildKQ0KYGBgDQoNCiMgTGlua2luZyAkUl97dCxtfSQgZXN0aW1hdGVzIHRvIG1vYmlsaXR5DQoNCiMjIFByZXBhcmluZyBgUnRfZGF0YWANCg0KTGlua2luZyB0aGUgbW9iaWxpdHkgZGF0YSBzaG91bGQgYmUgZG9uZSBpbiBhIHdheSB0aGF0IGNvcnJlc3BvbmRzIHRvIHRoZSB0aW1lIHRoZSBpbmZlY3Rpb24gc2hvdWxkIGhhdmUgb2NjdXJyZWQgbm90IHRoZSB0aW1lIG9mIGRlYXRoLiAgQmVsb3cgbmV3IGRhdGUgcmFuZ2VzIGFyZSBjYWxjdWxhdGVkIHRvIGNvcnJlc3BvbmQgcm91Z2hseSB0byB0aGUgdGltZSBvZiBpbmZlY3Rpb246DQoNCmBgYHtyIGNhbGNfZGF0ZXNfaW5mZWN0aW9ufQ0KUnRfZGF0YSRkYXRlX2luZl9zdGFydCA8LSBSdF9kYXRhJGRhdGVfc3RhcnQgLSBpZmVsc2UoUnRfZGF0YSR0eXBlPT0iZGVhdGgiLDIzKzcsNis3KQ0KUnRfZGF0YSRkYXRlX2luZl9lbmQgPC0gUnRfZGF0YSRkYXRlX2VuZCAtIGlmZWxzZShSdF9kYXRhJHR5cGU9PSJkZWF0aCIsMjMrNyw2KzcpDQpSdF9kYXRhJGRhdGVfaW5mX21pZCA8LSBSdF9kYXRhJGRhdGVfbWlkIC0gaWZlbHNlKFJ0X2RhdGEkdHlwZT09ImRlYXRoIiwyMys3LDYrNykNCmBgYA0KDQoNCiMjIENhbGN1bGF0aW5nIHdlZWtseSBtb2JpbGl0eSBkYXRhDQoNClRoZSBjb2RlIGJlbG93IGNhbGN1bGF0ZXMgYXZlcmFnZSB3ZWVrbHkgbW9iaWxpdHkgZGF0YSBhdmVyYWdlZCBvdmVyIHRoZSBzYW1lIGRhdGUgcmFuZ2VzIGFzIGNhbGN1bGF0ZWQgYWJvdmUuDQoNCmBgYHtyIGNhbGNfd2Vla2x5X21vYmlsaXR5fQ0Kd2Vla2x5X21vYmlsaXR5X2RhdGEgPC0gTlVMTA0KY29tYmluZWRfY291bnRyeV9jb2RlcyA8LQ0KICBsZXZlbHMoUnRfZGF0YSRjb3VudHJ5X2NvZGUpW2xldmVscyhSdF9kYXRhJGNvdW50cnlfY29kZSkgJWluJSBsZXZlbHMoZ29vZ2xlX21vYmlsaXR5X2RhdGEkY291bnRyeV9jb2RlKV0NCmZvciAoYyBpbiBjb21iaW5lZF9jb3VudHJ5X2NvZGVzKSB7DQogIGZvciAodCBpbiBsZXZlbHMoUnRfZGF0YSR0eXBlKSkgew0KICAgIGNfZGF0YSA8LSBSdF9kYXRhICU+JSBmaWx0ZXIoY291bnRyeV9jb2RlID09IGMgJiB0eXBlID09IHQpDQogICAgaWYgKG5yb3coY19kYXRhKSA+IDApIHsNCiAgICAgIGZvciAoaSBpbiAxOm5yb3coY19kYXRhKSkgew0KICAgICAgICB3X2RhdGEgPC0gZ29vZ2xlX21vYmlsaXR5X2RhdGEgJT4lIGZpbHRlcigNCiAgICAgICAgICBjb3VudHJ5X2NvZGUgPT0gYyAmDQogICAgICAgICAgICBkYXRlID49IGNfZGF0YSRkYXRlX2luZl9zdGFydFtpXSAmDQogICAgICAgICAgICBkYXRlIDw9IGNfZGF0YSRkYXRlX2luZl9lbmRbaV0NCiAgICAgICAgKSAlPiUNCiAgICAgICAgICBncm91cF9ieShjb3VudHJ5X2NvZGUpICU+JQ0KICAgICAgICAgIHN1bW1hcmlzZSgNCiAgICAgICAgICAgIHJldGFpbF9hbmRfcmVjcmVhdGlvbiA9IG1lYW4ocmV0YWlsX2FuZF9yZWNyZWF0aW9uKSwNCiAgICAgICAgICAgIGdyb2NlcnlfYW5kX3BoYXJtYWN5ID0gbWVhbihncm9jZXJ5X2FuZF9waGFybWFjeSksDQogICAgICAgICAgICBwYXJrcyA9IG1lYW4ocGFya3MpLA0KICAgICAgICAgICAgdHJhbnNpdF9zdGF0aW9ucyA9IG1lYW4odHJhbnNpdF9zdGF0aW9ucyksDQogICAgICAgICAgICB3b3JrcGxhY2VzID0gbWVhbih3b3JrcGxhY2VzKSwNCiAgICAgICAgICAgIHJlc2lkZW50aWFsID0gbWVhbihyZXNpZGVudGlhbCksDQogICAgICAgICAgICBhdmVyYWdlX21vYmlsaXR5ID0gbWVhbihhdmVyYWdlX21vYmlsaXR5KQ0KICAgICAgICAgICkgJT4lDQogICAgICAgICAgdW5ncm91cCgpDQogICAgICAgIGlmICghaXMubmEod19kYXRhJGNvdW50cnlfY29kZSkpIHsNCiAgICAgICAgICB3X2RhdGEkdHlwZSA8LSB0DQogICAgICAgICAgd19kYXRhJGRhdGVfaW5mX21pZCA8LSBjX2RhdGEkZGF0ZV9pbmZfbWlkW2ldDQogICAgICAgICAgd2Vla2x5X21vYmlsaXR5X2RhdGEgPC0gcmJpbmQod2Vla2x5X21vYmlsaXR5X2RhdGEsIHdfZGF0YSkNCiAgICAgICAgfQ0KICAgICAgfQ0KICAgIH0NCiAgfQ0KfQ0KYGBgDQoNCiMjIEpvaW5pbmcgd2l0aCAkUl97dCxtfSQgZXN0aW1hdGVzDQoNCkJhc2VkIG9uIHdvcmsgYWJvdmUgdGhlIHdlZWtseSBtb2JpbGl0eSBkYXRhIGNhbiBiZSBqb2luZWQ6DQpgYGB7ciBqb2luX21vYmlsaXR5fQ0KUnRfZGF0YSA8LQ0KICBsZWZ0X2pvaW4oUnRfZGF0YSwNCiAgICAgICAgICAgIHdlZWtseV9tb2JpbGl0eV9kYXRhLA0KICAgICAgICAgICAgYnkgPSBjKCJjb3VudHJ5X2NvZGUiLCAidHlwZSIsICJkYXRlX2luZl9taWQiKSkNCmBgYA0KDQojIE1vZGVsbGluZyAkUl97dCxtfSQgYXMgYSBmdW5jdGlvbiBvZiBNb2JpbGl0eSBJbmRleGVzDQoNCioqVE8gRE8qKg0KDQoNCiMgUmVzdWx0cw0KDQojIyBDdXJyZW50ICRSX3t0LG19JCBlc3RpbWF0ZXMNCg0KQmVsb3cgY3VycmVudCAobGFzdCB3ZWVrbHkpICRSX3t0LG19JCBlc3RpbWF0ZXMgYXJlIHRhYnVsYXRlZC4gIFRvIHNvIHRoZSBkYXRhIGZpcnN0IG5lZWRzIHRvIGJlIHByZXBhcmVkOg0KDQpgYGB7ciBwcmVwX3RhYmxlfQ0KIyBmaW5kIHRoZSBsYXN0IGVzdGltYXRlcw0KbGFzdF9kYXRlcyA8LSBSdF9kYXRhICU+JQ0KICBncm91cF9ieShjb3VudHJ5LCB0eXBlKSAlPiUNCiAgc3VtbWFyaXNlKGRhdGVfbWlkID0gbWF4KGRhdGVfbWlkKSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQojIGNvbnN0cnVjdCBhIHRhYmxlIHdpdGggbmljZSBmaWVsZHMgbmFtZXMNCnRhYmxlIDwtDQogIGlubmVyX2pvaW4obGFzdF9kYXRlcywgUnRfZGF0YSwgYnkgPSBjKCJjb3VudHJ5IiwgInR5cGUiLCAiZGF0ZV9taWQiKSkNCnRhYmxlIDwtIHRhYmxlICU+JQ0KICBzZWxlY3QoY29udGluZW50LCBjb3VudHJ5X2NvZGUsIGNvdW50cnksIHR5cGUsIGNvdW50LCBkYXRlX2VuZCwgUnRfbGlfOTUsIFJ0X21lYW4sIFJ0X3VpXzk1KQ0KYGBgDQoNCiMjIyBTb3V0aCBBZnJpY2ENCg0KYGBge3IgdGFidWxhdGVfemF9DQprbml0cjo6a2FibGUoKHRhYmxlICU+JSBmaWx0ZXIoY29udGluZW50ID09ICJTQSIpKVssIGMoLTEsLTIpXSwNCiAgICAgICAgICAgICBmb3JtYXQuYXJncyA9IGxpc3QoYmlnLm1hcmsgPSAnLCcpLA0KICAgICAgICAgICAgIGRpZ2l0cyA9IDEsDQogICAgICAgICAgICAgY29sLm5hbWVzID0gIGMoDQogICAgICAgICAgICAgICAiQ291bnRyeSIsDQogICAgICAgICAgICAgICAiRXN0aW1hdGVkIFR5cGUiLA0KICAgICAgICAgICAgICAgIkNvdW50IChMYXN0IFdlZWspIiwNCiAgICAgICAgICAgICAgICJXZWVrIEVuZGluZyIsDQogICAgICAgICAgICAgICAiUiAtIExvd2VyIENJIiwNCiAgICAgICAgICAgICAgICJSIC0gTWVhbiIsDQogICAgICAgICAgICAgICAiUiAtIFVwcHBlciBDSSINCiAgICAgICAgICAgICApDQopDQpgYGANCg0KIyMjIE90aGVyIENvdW50cmllcw0KDQpUaGUgdGFibGUgYmVsb3cgY29udGFpbnMgJFJfe3QsbX0kIGVzdGltYXRlcyBmb3IgdGhlIGxhc3Qgd2VlayBhdmFpbGFibGUuICBUaGVzZSBlc3RpbWF0ZXMgYXJlIGJhc2VkIGVpdGhlciBvbiBjYXNlcyBvciBkZWF0aHMgYW5kIGEgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgaXMgc2hvd24uDQoNCmBgYHtyIHRhYnVsYXRlfQ0Ka25pdHI6OmthYmxlKCh0YWJsZSAlPiUgZmlsdGVyKGNvbnRpbmVudCAhPSAiU0EiICYgY291bnRyeV9jb2RlICVpbiUgY291bnRyeV9jb2RlcykpWywgYygtMSwtMildLA0KICAgICAgICAgICAgIGZvcm1hdC5hcmdzID0gbGlzdChiaWcubWFyayA9ICcsJyksDQogICAgICAgICAgICAgZGlnaXRzID0gMSwNCiAgICAgICAgICAgICBjb2wubmFtZXMgPSAgYygNCiAgICAgICAgICAgICAgICJDb3VudHJ5IiwNCiAgICAgICAgICAgICAgICJFc3RpbWF0ZWQgVHlwZSIsDQogICAgICAgICAgICAgICAiQ291bnQgKExhc3QgV2VlaykiLA0KICAgICAgICAgICAgICAgIldlZWsgRW5kaW5nIiwNCiAgICAgICAgICAgICAgICJSIC0gTG93ZXIgQ0kiLA0KICAgICAgICAgICAgICAgIlIgLSBNZWFuIiwNCiAgICAgICAgICAgICAgICJSIC0gVXBwcGVyIENJIg0KICAgICAgICAgICAgICkNCikNCmBgYA0KDQojIyMgVG9wIDEwIENvdW50cmllcw0KDQpCZWxvdyB3ZSBzaG93IHZhcmlvdXMgZXh0cmVtZXMgb2YgJFJfe3QsbX0kIHdoZXJlIGNvdW50cyAoZGVhdGhzIG9yIGNhc2VzKSBleGNlZWQgNTAgaW4gdGhlIGxhc3Qgd2Vlay4NCg0KIyMjIyBMb3dlc3QgJFJfe3QsbX0kIGJhc2VkIG9uIERlYXRocw0KDQpgYGB7ciB0YWJ1bGF0ZV9sb3dfZGVhdGhzfQ0Ka25pdHI6OmthYmxlKCh0YWJsZSAlPiUgZmlsdGVyKGNvbnRpbmVudCAhPSAiU0EiICYgdHlwZT09ImRlYXRocyIgJiBjb3VudD41MCkgJT4lDQogICAgICAgICAgICAgICAgYXJyYW5nZShSdF9tZWFuKSlbMToxMCwgYygtMSwtMildLA0KICAgICAgICAgICAgIGZvcm1hdC5hcmdzID0gbGlzdChiaWcubWFyayA9ICcsJyksDQogICAgICAgICAgICAgZGlnaXRzID0gMSwNCiAgICAgICAgICAgICBjb2wubmFtZXMgPSAgYygNCiAgICAgICAgICAgICAgICJDb3VudHJ5IiwNCiAgICAgICAgICAgICAgICJFc3RpbWF0ZWQgVHlwZSIsDQogICAgICAgICAgICAgICAiQ291bnQgKExhc3QgV2VlaykiLA0KICAgICAgICAgICAgICAgIldlZWsgRW5kaW5nIiwNCiAgICAgICAgICAgICAgICJSIC0gTG93ZXIgQ0kiLA0KICAgICAgICAgICAgICAgIlIgLSBNZWFuIiwNCiAgICAgICAgICAgICAgICJSIC0gVXBwcGVyIENJIg0KICAgICAgICAgICAgICkNCikNCmBgYA0KDQojIyMjIExvd2VzdCAkUl97dCxtfSQgYmFzZWQgb24gQ2FzZXMNCg0KYGBge3IgdGFidWxhdGVfbG93X2Nhc2VzfQ0Ka25pdHI6OmthYmxlKCh0YWJsZSAlPiUgZmlsdGVyKGNvbnRpbmVudCAhPSAiU0EiICYgdHlwZT09ImNhc2VzIiAmIGNvdW50PjUwKSAlPiUNCiAgICAgICAgICAgICAgICBhcnJhbmdlKFJ0X21lYW4pKVsxOjEwLCBjKC0xLC0yKV0sDQogICAgICAgICAgICAgZm9ybWF0LmFyZ3MgPSBsaXN0KGJpZy5tYXJrID0gJywnKSwNCiAgICAgICAgICAgICBkaWdpdHMgPSAxLA0KICAgICAgICAgICAgIGNvbC5uYW1lcyA9ICBjKA0KICAgICAgICAgICAgICAgIkNvdW50cnkiLA0KICAgICAgICAgICAgICAgIkVzdGltYXRlZCBUeXBlIiwNCiAgICAgICAgICAgICAgICJDb3VudCAoTGFzdCBXZWVrKSIsDQogICAgICAgICAgICAgICAiV2VlayBFbmRpbmciLA0KICAgICAgICAgICAgICAgIlIgLSBMb3dlciBDSSIsDQogICAgICAgICAgICAgICAiUiAtIE1lYW4iLA0KICAgICAgICAgICAgICAgIlIgLSBVcHBwZXIgQ0kiDQogICAgICAgICAgICAgKQ0KKQ0KYGBgDQojIyMjIEhpZ2hlc3QgJFJfe3QsbX0kIGJhc2VkIG9uIERlYXRocw0KDQpgYGB7ciB0YWJ1bGF0ZV9oaWdoX2RlYXRoc30NCmtuaXRyOjprYWJsZSgodGFibGUgJT4lIGZpbHRlcihjb250aW5lbnQgIT0gIlNBIiAmIHR5cGU9PSJkZWF0aHMiICYgY291bnQ+NTApICU+JQ0KICAgICAgICAgICAgICAgIGFycmFuZ2UoLVJ0X21lYW4pKVsxOjEwLCBjKC0xLC0yKV0sDQogICAgICAgICAgICAgZm9ybWF0LmFyZ3MgPSBsaXN0KGJpZy5tYXJrID0gJywnKSwNCiAgICAgICAgICAgICBkaWdpdHMgPSAxLA0KICAgICAgICAgICAgIGNvbC5uYW1lcyA9ICBjKA0KICAgICAgICAgICAgICAgIkNvdW50cnkiLA0KICAgICAgICAgICAgICAgIkVzdGltYXRlZCBUeXBlIiwNCiAgICAgICAgICAgICAgICJDb3VudCAoTGFzdCBXZWVrKSIsDQogICAgICAgICAgICAgICAiV2VlayBFbmRpbmciLA0KICAgICAgICAgICAgICAgIlIgLSBMb3dlciBDSSIsDQogICAgICAgICAgICAgICAiUiAtIE1lYW4iLA0KICAgICAgICAgICAgICAgIlIgLSBVcHBwZXIgQ0kiDQogICAgICAgICAgICAgKQ0KKQ0KYGBgDQoNCiMjIyMgSGlnaGVzdCAkUl97dCxtfSQgYmFzZWQgb24gQ2FzZXMNCg0KYGBge3IgdGFidWxhdGVfaGlnaF9jYXNlc30NCmtuaXRyOjprYWJsZSgodGFibGUgJT4lIGZpbHRlcihjb250aW5lbnQgIT0gIlNBIiAmIHR5cGU9PSJjYXNlcyIgJiBjb3VudD41MCkgJT4lDQogICAgICAgICAgICAgICAgYXJyYW5nZSgtUnRfbWVhbikpWzE6MTAsIGMoLTEsLTIpXSwNCiAgICAgICAgICAgICBmb3JtYXQuYXJncyA9IGxpc3QoYmlnLm1hcmsgPSAnLCcpLA0KICAgICAgICAgICAgIGRpZ2l0cyA9IDEsDQogICAgICAgICAgICAgY29sLm5hbWVzID0gIGMoDQogICAgICAgICAgICAgICAiQ291bnRyeSIsDQogICAgICAgICAgICAgICAiRXN0aW1hdGVkIFR5cGUiLA0KICAgICAgICAgICAgICAgIkNvdW50IChMYXN0IFdlZWspIiwNCiAgICAgICAgICAgICAgICJXZWVrIEVuZGluZyIsDQogICAgICAgICAgICAgICAiUiAtIExvd2VyIENJIiwNCiAgICAgICAgICAgICAgICJSIC0gTWVhbiIsDQogICAgICAgICAgICAgICAiUiAtIFVwcHBlciBDSSINCiAgICAgICAgICAgICApDQopDQpgYGANCg0KIyMgUmVzdWx0IFBsb3RzDQoNCkxldCdzIGdlbmVyYXRlIG91ciBwbG90cyBmb3IgZWFjaCBjb3VudHJ5L3Byb3ZpbmNlIGluIGEgbGlzdC4gIFdlIGZpbHRlciBvdXQgd2Vla3Mgd2hlcmUgdGhlIHVwcGVyIGVuZCBvZiBjb25maWRlbmNlIGludGVydmFsIGZvciAkUl97dCxtfSBleGNlZWRzIGZpdmUuDQoNCmBgYHtyIHJlc3VsdF9wbG90c30NCmNvdW50cnlfcGxvdHMgPC0gbGlzdCgpDQpmb3IgKGMgaW4gbGV2ZWxzKFJ0X2RhdGEkY291bnRyeSkpIHsNCiAgcDEgPC0gZ2dwbG90KFJ0X2NpX2RhdGEgJT4lIGZpbHRlcihjb3VudHJ5ID09IGMgJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2kgPT0gIjk1JSIgJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnRfdWkgPD0gNSksDQogICAgICAgICAgICAgICBhZXMoeCA9IGRhdGVfbWlkLCB5ID0gUnRfbWVhbikpICsNCiAgICBnZW9tX2Nyb3NzYmFyKA0KICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZTIocGFkZGluZyA9IDApLA0KICAgICAgYWVzKA0KICAgICAgICB5bWluID0gUnRfbGksDQogICAgICAgIHltYXggPSBSdF91aSwNCiAgICAgICAgY29sb3VyID0gdHlwZSwNCiAgICAgICAgZmlsbCA9IHR5cGUNCiAgICAgICksDQogICAgICB3aWR0aCA9IDcNCiAgICApICsNCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKGFscGhhKCJkZWVwc2t5Ymx1ZTQiLCAwLjQ1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhKCIjOGIwMDY4IiwgMC40NSkpKSArDQogICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKGFscGhhKCJkZWVwc2t5Ymx1ZTQiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEoIiM4YjAwNjgiKSkpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA1KSkgKw0KICAgIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIG1vbnRocyIsIGxhYmVscyA9IGRhdGVfZm9ybWF0KCIlZSAlYiIpKSArDQogICAgdGhlbWVfYncoKSArDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwNCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArDQogICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobmNvbCA9IDEpKSArDQogICAgeGxhYigiV2VlayIpICsNCiAgICB5bGFiKGV4cHJlc3Npb24oUlt0LCBtXSkpICArDQogICAgZ2d0aXRsZShjKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEpDQogIGNvdW50cnlfcGxvdHNbW2NdXSA8LSBwMQ0KfQ0KYGBgDQoNCg0KIyMjIFNvdXRoIEFmcmljYQ0KDQpCZWxvdyB3ZSBwbG90IGFsbCB0aGUgcHJvdmluY2VzOg0KDQpgYGB7ciBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTJ9DQpnZ2FycmFuZ2UoDQogIGNvdW50cnlfcGxvdHNbWyJFQyJdXSwNCiAgY291bnRyeV9wbG90c1tbIkZTIl1dLA0KICBjb3VudHJ5X3Bsb3RzW1siR1AiXV0sDQogIGNvdW50cnlfcGxvdHNbWyJLWk4iXV0sDQogIGNvdW50cnlfcGxvdHNbWyJMUCJdXSwNCiAgY291bnRyeV9wbG90c1tbIk1QIl1dLA0KICBjb3VudHJ5X3Bsb3RzW1siTkMiXV0sDQogIGNvdW50cnlfcGxvdHNbWyJOVyJdXSwNCiAgY291bnRyeV9wbG90c1tbIldDIl1dLA0KICBjb3VudHJ5X3Bsb3RzW1siU0EiXV0sDQogIG5jb2wgPSAyLA0KICBucm93ID0gNQ0KKSArDQogIGdndGl0bGUoIlJlcHJvZHVjdGl2ZSBudW1iZXIgYnkgcHJvdmluY2UsIHdpdGggOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIikNCmBgYA0KDQojIyMgT3RoZXIgQ291bnRyaWVzDQoNCkJlbG93IHdlIHBsb3QgYWxsIHRoZSBjb3VudHJpZXM6DQoNCmBgYHtyIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xMn0NCmdnYXJyYW5nZSgNCiAgY291bnRyeV9wbG90c1tbIkJyYXppbCJdXSwNCiAgY291bnRyeV9wbG90c1tbIkNhbmFkYSJdXSwNCiAgY291bnRyeV9wbG90c1tbIkNoaWxlIl1dLA0KICBjb3VudHJ5X3Bsb3RzW1siSW5kaWEiXV0sDQogIGNvdW50cnlfcGxvdHNbWyJJcmVsYW5kIl1dLA0KICBjb3VudHJ5X3Bsb3RzW1siSXRhbHkiXV0sDQogIGNvdW50cnlfcGxvdHNbWyJQZXJ1Il1dLA0KICBjb3VudHJ5X3Bsb3RzW1siU291dGhfQWZyaWNhIl1dLA0KICBjb3VudHJ5X3Bsb3RzW1siU3BhaW4iXV0sDQogIGNvdW50cnlfcGxvdHNbWyJVbml0ZWRfS2luZ2RvbSJdXSwNCiAgbmNvbCA9IDIsDQogIG5yb3cgPSA1DQopICsNCiAgZ2d0aXRsZSgiUmVwcm9kdWN0aXZlIG51bWJlciBieSBjb3VudHJ5LCB3aXRoIDk1JSBjb25maWRlbmNlIGludGVydmFscyIpDQpgYGANCg0KIyMjIFNjYXR0ZXIgUGxvdHMgb2YgJFJfe3QsbX1ee2Nhc2VzfSQgdnMuICRSX3t0LG19XntkZWF0aHN9JA0KDQpCZWxvdyB3ZSBwbG90IGVzdGltYXRlcyBvZiAkUl97dCxtfSQgYmFzZWQgb24gY2FzZXMgdmVyc3VzIHRob3NlIGJhc2VkIG9uIGRlYXRocyBmb3IgY291bnRyaWVzIHdoZXJlIHRoZXNlIGVzdGltYXRlcyByYW5nZSBiZXR3ZWVuIDAuNSBhbmQgMi4NCg0KIyMjIyBTb3V0aCBBZnJpY2ENCg0KQmVsb3cgd2UgcGxvdCB3aGVyZSB0aGVyZSB3ZXJlIG1vcmUgdGhhbiA1IGRlYXRocyBhbmQgY2FzZXMgaW4gdGhlIGxhc3Qgd2Vlay4gIEl0IGFwcGVhcnMgdGhhdCwgZ2VuZXJhbGx5LCAkUl97dCxtfV57Y2FzZXN9JCBhbmQgJFJfe3QsbX1ee2RlYXRoc30kIGFyZSBjb25zaXN0ZW50Lg0KDQpgYGB7ciBzY2F0dGVyX3Bsb3RfemF9DQpzcF9kYXRhIDwtIHBpdm90X3dpZGVyKA0KICB0YWJsZSAlPiUgZmlsdGVyKGNvdW50Pj01ICYgY29udGluZW50ID09ICJTQSIpLCANCiAgaWRfY29scz1jKCJjb250aW5lbnQiLCJjb3VudHJ5IiksDQogIG5hbWVzX2Zyb20gPSAidHlwZSIsDQogIHZhbHVlc19mcm9tID0gIlJ0X21lYW4iDQopDQoNCmdncGxvdCgNCiAgc3BfZGF0YSwNCiAgYWVzKHggPSBjYXNlcywgeSA9IGRlYXRocykpICsNCiAgZ2VvbV9wb2ludChhZXMobGFiZWw9Y291bnRyeSkpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1jb3VudHJ5KSxoanVzdD0wLCB2anVzdD0wLCBzaXplPTIpICsNCiAgZ2d0aXRsZSgiUmVwcm9kdWN0aXZlIG51bWJlciBieSBjb3VudHJ5IikgKw0KICB4bGFiKGV4cHJlc3Npb24oUlt0LG1dXmNhc2VzKSkgKw0KICB5bGFiKGV4cHJlc3Npb24oUlt0LG1dXmRlYXRocykpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMC41LCAyKSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLjUsIDIpKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSArDQogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG5jb2wgPSAxKSkgKw0KICAgIHNjYWxlX2NvbG9yX2h1ZShsID0gNTApICsNCiAgZ2VvbV9hYmxpbmUoYT0wLGI9MSkNCmBgYA0KDQojIyMjIE90aGVyIGNvdW50cmllcw0KDQpCZWxvdyB3ZSBwbG90IHdoZXJlIHRoZXJlIHdlcmUgbW9yZSB0aGFuIDI1IGRlYXRocyBhbmQgY2FzZXMgaW4gdGhlIGxhc3Qgd2Vlay4gIEl0IGFwcGVhcnMgdGhhdCwgZ2VuZXJhbGx5LCAkUl97dCxtfV57Y2FzZXN9JCBhbmQgJFJfe3QsbX1ee2RlYXRoc30kIGFyZSBjb25zaXN0ZW50Lg0KDQpgYGB7ciBzY2F0dGVyX3Bsb3R9DQpzcF9kYXRhIDwtIHBpdm90X3dpZGVyKA0KICB0YWJsZSAlPiUgZmlsdGVyKGNvdW50Pj0yNSAmIGNvbnRpbmVudCAhPSAiU0EiKSwgDQogIGlkX2NvbHM9YygiY29udGluZW50IiwiY291bnRyeSIpLA0KICBuYW1lc19mcm9tID0gInR5cGUiLA0KICB2YWx1ZXNfZnJvbSA9ICJSdF9tZWFuIg0KKQ0KDQpnZ3Bsb3QoDQogIHNwX2RhdGEsDQogIGFlcyh4ID0gY2FzZXMsIHkgPSBkZWF0aHMpKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY29udGluZW50LCBsYWJlbD1jb3VudHJ5KSkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPWNvdW50cnkpLGhqdXN0PTAsIHZqdXN0PTAsIHNpemU9MikgKw0KICBnZ3RpdGxlKCJSZXByb2R1Y3RpdmUgbnVtYmVyIGJ5IGNvdW50cnkiKSArDQogIHhsYWIoZXhwcmVzc2lvbihSW3QsbV1eY2FzZXMpKSArDQogIHlsYWIoZXhwcmVzc2lvbihSW3QsbV1eZGVhdGhzKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLjUsIDIpKSArDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAuNSwgMikpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobmNvbCA9IDEpKSArDQogICAgc2NhbGVfY29sb3JfaHVlKGwgPSA1MCkgKw0KICBnZW9tX2FibGluZShhPTAsYj0xKQ0KYGBgDQoNCiMjIE1vYmlsaXR5DQoNCiMjIyBTaW1wbGUgcGxvdA0KDQpCZWxvdyB3ZSBwbG90IHdlZWtzIG9mIGVzdGltYXRlcyBvZiAkUl97dCxtfSQgdmVyc3VzIHRoZSBhdmVyYWdlIG1vYmlsaXR5IGluZGV4IHRoYXQgd2FzIGNvbnN0cnVjdGVkLiBUaGVyZSBpcyBhIGNsZWFyIGxpbmsgYmV0d2VlbiBtb2JpbGl0eSBhbmQgdGhlIHJlcHJvZHVjdGl2ZSBudW1iZXIuICBBcyBpdCBkZWNyZWFzZXMgJFJfe3QsbX0kIHJlZHVjZXM6DQoNCmBgYHtyIHBsb3RfbW9iaWxpdHlfdnNfUnRfZGVhdGhzfQ0KZ2dwbG90KA0KICBSdF9kYXRhICU+JSBmaWx0ZXIodHlwZT09ImNhc2VzIiAmIGNvdW50PjEwMCAmIA0KICAgICAgICAgICAgICAgICAgICAgICBjb3VudHJ5X2NvZGUgJWluJSBjKGNvdW50cnlfY29kZXMpKSwNCiAgYWVzKHggPSBhdmVyYWdlX21vYmlsaXR5LCB5ID0gUnRfbWVhbikpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyPWNvdW50cnkpKSArDQogIGdndGl0bGUoIlJlcHJvZHVjdGl2ZSBudW1iZXIgYnkgbW9iaWxpdHkiKSArDQogIHlsYWIoZXhwcmVzc2lvbihSW3QsbV1eY2FzZXMpKSArDQogICNzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAxMCkpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobmNvbCA9IDEpKSArDQogICAgc2NhbGVfY29sb3JfaHVlKGwgPSA1MCkNCmBgYA0KDQojIyMgTW9kZWxsaW5nIFJlc3VsdHMNCg0KKioqVE8gRE8qKioNCg0KIyBEaXNjdXNzaW9uDQoNCkZyb20gdGhlIGJhc2ljIHBsb3RzIGl0J3MgY2xlYXIgdGhhdCBTb3V0aCBBZnJpY2EgYW5kIGEgZmV3IG90aGVyIGNvdW50cmllcyBhcHBlYXIgdG8gYmUgb24gYSBkaWZmZXJlbnQgInNsb3BlIiB0aGFuIEV1cm9wZWFuIGNvdW50cmllcyBzaG93LiAgVGhlc2UgaW5jbHVkZSBCcmF6aWwsIFBlcnUsIENoaWxlIGFuZCBJbmRpYS4NCg0KVGhlIGFib3ZlIHNob3dzIGVzdGltYXRlcyBmb3IgcmVwcm9kdWN0aXZlIG51bWJlciB1c2luZyBjYXNlcyBhbmQgZGVhdGhzICgkUl97dCxtfV57Y2FzZXN9JCBhbmQgJFJfe3QsbX1ee2RlYXRoc30kKSBmb3IgZWFjaCBjb3VudHJ5ICRtJCBvdmVyIHRpbWUgJHQkIGluIHdlZWtzLg0KDQpGcm9tIHRoZSBjdXJyZW50IGVzdGltYXRlcyBpdCBhcHBlYXJzIHRoYXQsIGF0IHByZXNlbnQsIGluIGdlbmVyYWwgU291dGggQWZyaWNhbiBwcm92aW5jaWFsIGVzdGltYXRlcyBiYXNlZCBvbiBjYXNlcyBhbmQgZGVhdGhzIGNvcnJlc3BvbmQuICBBbiBleGNlcHRpb24gdG8gdGhpcyBtYXkgYmUgRWFzdGVybiBDYXBlIHdpdGggZXN0aW1hdGVzIGJhc2VkIG9uIGRlYXRocyBleGNlZWRpbmcgdGhvc2UgYmFzZWQgb24gY2FzZXMuDQoNCkl0IGlzIGFsc28gY2xlYXIgdGhhdCBlc3RpbWF0ZXMgYmFzZWQgb24gY2FzZXMgYXBwZWFyIHRvIHByZWNlZGUgdGhlIG1vdmVtZW50cyBiYXNlZCBvbiBkZWF0aHMgb3ZlciB0aW1lLiAgVGhpcyBjYW4gYmUgZXhwZWN0ZWQgYXMsIGFsbCB0aGluZ3MgYmVpbmcgZXF1YWwsIGluZmVjdGlvbnMgYW5kIGFzc29jaWF0ZWQgY2FzZXMgc2hvdWxkIGJlIGEgcHJlY3Vyc29yIHRvIGRlYXRocy4NCg0KU291dGggQWZyaWNhIGRvZXMgbm90IGNvbXBhcmUgd2VsbCwgYW5kIGFwcGVhcnMgdG8gaGF2ZSBvbmUgb2YgdGhlIGhpZ2hlc3QgJFJfe3QsbX1ee2RlYXRoc30kIGluIHRoZSB3b3JsZC4gIFRoaXMgaXMgaGlnaGVyIHRoYW4gdGhlICRSX3t0LG19XntjYXNlc30kIHdoaWNoIG1heSBiZSBpbmRpY2F0aXZlIG9mIGxvd2VyIHRlc3RpbmcgYW5kL29yIGJhY2tsb2dzIHdpdGggcmVnYXJkIHRvIHRlc3RpbmcuDQoNCk9uIGEgc2NhdHRlciBwbG90IG9mIGNvdW50cmllcyBtb3N0IGFwcGVhciB0byBoYXZlICRSX3t0LG19XntjYXNlc30kIGNvcnJlbGF0ZWQgd2VsbCB3aXRoICRSX3t0LG19XntkZWF0aHN9JC4gIENoaWxlLCBBZmdoYW5pc3RhbiBhbmQgSXJhbiBhcHBlYXIgdG8gYmUgb3V0bGllcnMgd2l0aCAkUl97dCxtfV57ZGVhdGhzfSBoaWdoZXIgdGhhbiAkUl97dCxtfV57Y2FzZXN9JCBpbmRpY2F0aW5nIHBlcmhhcHMgbGltaXRlZCB0ZXN0aW5nLCBvciBhbHRlcm5hdGl2ZWx5IGVwaWRlbWljcyB0aGF0IGFyZSBzbG93aW5nIChnaXZlbiB0aGUgbGVhZGluZyBuYXR1cmUgb2YgY2FzZXMgdnMuIGRlYXRocykuDQoNCk92ZXJhbGwgaXQncyBjbGVhciB0aGF0IGhhdmluZyBtdWx0aXBsZSBtZWFzdXJlcyBvZiAkUl97dCxtfSQgYXBwZWFycyB1c2VmdWwgYW5kIG1vbml0b3JpbmcgaXQncyB2YWx1ZSBpcyBzb21ldGhpbmcgdXNlZnVsLg0KDQojIERldGFpbGVkIE91dHB1dA0KDQpEZXRhaWxlZCBvdXRwdXQgZm9yIGFsbCBjb3VudHJpZXMgYXJlIHNhdmVkIHRvIGEgY29tbWEtc2VwYXJhdGVkIHZhbHVlIGZpbGUgYmVsb3cuICBUaGUgZmlsZSBbY2FuIGJlIGZvdW5kIGhlcmVdKFJ0X2RhdGEuY3N2KS4NCg0KYGBge3Igd3JpdGVfY3N2fQ0Kd3JpdGUuY3N2KFJ0X2RhdGEsIlJ0X2RhdGEuY3N2Iixyb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIEF1dGhvcg0KDQpUaGlzIHJlcG9ydCB3YXMgcHJlcGFyZWQgYnkgTG91aXMgUm9zc291dy4gIFBsZWFzZSBnZXQgaW4gY29udGFjdCB3aXRoIExvdWlzIFJvc3NvdXcgaWYgeW91IGhhdmUgY29tbWVudHMgb3Igd2lzaCB0byByZWNlaXZlIHRoaXMgcmVndWxhcmx5Lg0KDQoqKkxvdWlzIFJvc3NvdXcqKiAgDQpIZWFkIG9mIFJlc2VhcmNoICYgQW5hbHl0aWNzICANCkdlbiBSZSB8IExpZmUvSGVhbHRoIENhbmFkYSwgU291dGggQWZyaWNhLCBBdXN0cmFsaWEsIE5aLCBVSyAmIElyZWxhbmQgIA0KRW1haWw6IFtMUm9zc291d0BHZW5SZS5jb21dKG1haWx0bzpMUm9zc291d0BnZW5yZS5jb20pIE1vYmlsZTogKzI3IDcxIDM1NSAyNTUwDQoNCioqKlRoZSB2aWV3cyBpbiB0aGlzIGRvY3VtZW50IHJlcHJlc2VudHMgdGhhdCBvZiB0aGUgYXV0aG9yIGFuZCBtYXkgbm90IHJlcHJlc2VudCB0aG9zZSBvZiBHZW4gUmUuICBBbHNvIG5vdGUgdGhhdCBnaXZlbiB0aGUgc2lnbmlmaWNhbnQgdW5jZXJ0YWludHkgaW52b2x2ZWQgd2l0aCB0aGUgcGFyYW1ldGVycywgZGF0YSBhbmQgbWV0aG9kb2xvZ3kgY2FyZSBzaG91bGQgYmUgdGFrZW4gd2l0aCB0aGVzZSBudW1iZXJzIGFuZCBhbnkgdXNlIG9mIHRoZXNlIG51bWJlcnMuKioqDQoNCiMgUmVmZXJlbmNlcw0K